1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "fxjs/xfa/cfxjse_formcalc_context.h"
8
9 #include <ctype.h>
10 #include <math.h>
11 #include <stdint.h>
12 #include <stdlib.h>
13
14 #include <algorithm>
15 #include <limits>
16 #include <memory>
17 #include <utility>
18 #include <vector>
19
20 #include "core/fxcrt/cfx_datetime.h"
21 #include "core/fxcrt/data_vector.h"
22 #include "core/fxcrt/fx_extension.h"
23 #include "core/fxcrt/fx_random.h"
24 #include "core/fxcrt/fx_safe_types.h"
25 #include "core/fxcrt/widetext_buffer.h"
26 #include "fxjs/fxv8.h"
27 #include "fxjs/xfa/cfxjse_class.h"
28 #include "fxjs/xfa/cfxjse_context.h"
29 #include "fxjs/xfa/cfxjse_engine.h"
30 #include "fxjs/xfa/cfxjse_value.h"
31 #include "fxjs/xfa/cjx_object.h"
32 #include "third_party/abseil-cpp/absl/types/optional.h"
33 #include "third_party/base/check_op.h"
34 #include "third_party/base/cxx17_backports.h"
35 #include "third_party/base/numerics/safe_conversions.h"
36 #include "v8/include/v8-container.h"
37 #include "v8/include/v8-function-callback.h"
38 #include "v8/include/v8-object.h"
39 #include "v8/include/v8-primitive.h"
40 #include "xfa/fgas/crt/cfgas_decimal.h"
41 #include "xfa/fxfa/cxfa_ffnotify.h"
42 #include "xfa/fxfa/formcalc/cxfa_fmparser.h"
43 #include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
44 #include "xfa/fxfa/parser/cxfa_document.h"
45 #include "xfa/fxfa/parser/cxfa_localevalue.h"
46 #include "xfa/fxfa/parser/cxfa_node.h"
47 #include "xfa/fxfa/parser/cxfa_thisproxy.h"
48 #include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
49 #include "xfa/fxfa/parser/gced_locale_iface.h"
50 #include "xfa/fxfa/parser/xfa_utils.h"
51
52 using pdfium::fxjse::kClassTag;
53 using pdfium::fxjse::kFuncTag;
54
55 namespace {
56
57 // Maximum number of characters Acrobat can fit in a text box.
58 constexpr int kMaxCharCount = 15654908;
59
60 const double kFinancialPrecision = 0.00000001;
61
62 const wchar_t kStrCode[] = L"0123456789abcdef";
63
64 struct XFA_FMHtmlReserveCode {
65 uint16_t m_uCode;
66 // Inline string data reduces size for small strings.
67 const char m_htmlReserve[10];
68 };
69
70 // Sorted by |m_htmlReserve|.
71 const XFA_FMHtmlReserveCode kReservesForDecode[] = {
72 {198, "AElig"}, {193, "Aacute"}, {194, "Acirc"}, {192, "Agrave"},
73 {913, "Alpha"}, {197, "Aring"}, {195, "Atilde"}, {196, "Auml"},
74 {914, "Beta"}, {199, "Ccedil"}, {935, "Chi"}, {8225, "Dagger"},
75 {916, "Delta"}, {208, "ETH"}, {201, "Eacute"}, {202, "Ecirc"},
76 {200, "Egrave"}, {917, "Epsilon"}, {919, "Eta"}, {203, "Euml"},
77 {915, "Gamma"}, {922, "Kappa"}, {923, "Lambda"}, {924, "Mu"},
78 {209, "Ntilde"}, {925, "Nu"}, {338, "OElig"}, {211, "Oacute"},
79 {212, "Ocirc"}, {210, "Ograve"}, {937, "Omega"}, {927, "Omicron"},
80 {216, "Oslash"}, {213, "Otilde"}, {214, "Ouml"}, {934, "Phi"},
81 {928, "Pi"}, {936, "Psi"}, {929, "Rho"}, {352, "Scaron"},
82 {931, "Sigma"}, {222, "THORN"}, {932, "Tau"}, {920, "Theta"},
83 {218, "Uacute"}, {219, "Ucirc"}, {217, "Ugrave"}, {933, "Upsilon"},
84 {220, "Uuml"}, {926, "Xi"}, {221, "Yacute"}, {376, "Yuml"},
85 {918, "Zeta"}, {225, "aacute"}, {226, "acirc"}, {180, "acute"},
86 {230, "aelig"}, {224, "agrave"}, {8501, "alefsym"}, {945, "alpha"},
87 {38, "amp"}, {8743, "and"}, {8736, "ang"}, {39, "apos"},
88 {229, "aring"}, {8776, "asymp"}, {227, "atilde"}, {228, "auml"},
89 {8222, "bdquo"}, {946, "beta"}, {166, "brvbar"}, {8226, "bull"},
90 {8745, "cap"}, {231, "ccedil"}, {184, "cedil"}, {162, "cent"},
91 {967, "chi"}, {710, "circ"}, {9827, "clubs"}, {8773, "cong"},
92 {169, "copy"}, {8629, "crarr"}, {8746, "cup"}, {164, "current"},
93 {8659, "dArr"}, {8224, "dagger"}, {8595, "darr"}, {176, "deg"},
94 {948, "delta"}, {9830, "diams"}, {247, "divide"}, {233, "eacute"},
95 {234, "ecirc"}, {232, "egrave"}, {8709, "empty"}, {8195, "emsp"},
96 {8194, "ensp"}, {949, "epsilon"}, {8801, "equiv"}, {951, "eta"},
97 {240, "eth"}, {235, "euml"}, {8364, "euro"}, {8707, "exist"},
98 {402, "fnof"}, {8704, "forall"}, {189, "frac12"}, {188, "frac14"},
99 {190, "frac34"}, {8260, "frasl"}, {947, "gamma"}, {8805, "ge"},
100 {62, "gt"}, {8660, "hArr"}, {8596, "harr"}, {9829, "hearts"},
101 {8230, "hellip"}, {237, "iacute"}, {238, "icirc"}, {161, "iexcl"},
102 {236, "igrave"}, {8465, "image"}, {8734, "infin"}, {8747, "int"},
103 {953, "iota"}, {191, "iquest"}, {8712, "isin"}, {239, "iuml"},
104 {954, "kappa"}, {8656, "lArr"}, {205, "lacute"}, {955, "lambda"},
105 {9001, "lang"}, {171, "laquo"}, {8592, "larr"}, {8968, "lceil"},
106 {206, "lcirc"}, {8220, "ldquo"}, {8804, "le"}, {8970, "lfloor"},
107 {204, "lgrave"}, {921, "lota"}, {8727, "lowast"}, {9674, "loz"},
108 {8206, "lrm"}, {8249, "lsaquo"}, {8216, "lsquo"}, {60, "lt"},
109 {207, "luml"}, {175, "macr"}, {8212, "mdash"}, {181, "micro"},
110 {183, "middot"}, {8722, "minus"}, {956, "mu"}, {8711, "nabla"},
111 {160, "nbsp"}, {8211, "ndash"}, {8800, "ne"}, {8715, "ni"},
112 {172, "not"}, {8713, "notin"}, {8836, "nsub"}, {241, "ntilde"},
113 {957, "nu"}, {243, "oacute"}, {244, "ocirc"}, {339, "oelig"},
114 {242, "ograve"}, {8254, "oline"}, {969, "omega"}, {959, "omicron"},
115 {8853, "oplus"}, {8744, "or"}, {170, "ordf"}, {186, "ordm"},
116 {248, "oslash"}, {245, "otilde"}, {8855, "otimes"}, {246, "ouml"},
117 {182, "para"}, {8706, "part"}, {8240, "permil"}, {8869, "perp"},
118 {966, "phi"}, {960, "pi"}, {982, "piv"}, {177, "plusmn"},
119 {8242, "prime"}, {8719, "prod"}, {8733, "prop"}, {968, "psi"},
120 {163, "pund"}, {34, "quot"}, {8658, "rArr"}, {8730, "radic"},
121 {9002, "rang"}, {187, "raquo"}, {8594, "rarr"}, {8969, "rceil"},
122 {8476, "real"}, {174, "reg"}, {8971, "rfloor"}, {961, "rho"},
123 {8207, "rlm"}, {8250, "rsaquo"}, {8217, "rsquo"}, {353, "saron"},
124 {8218, "sbquo"}, {8901, "sdot"}, {167, "sect"}, {173, "shy"},
125 {963, "sigma"}, {962, "sigmaf"}, {8764, "sim"}, {9824, "spades"},
126 {8834, "sub"}, {8838, "sube"}, {8721, "sum"}, {8835, "sup"},
127 {185, "sup1"}, {178, "sup2"}, {179, "sup3"}, {8839, "supe"},
128 {223, "szlig"}, {964, "tau"}, {8221, "tdquo"}, {8756, "there4"},
129 {952, "theta"}, {977, "thetasym"}, {8201, "thinsp"}, {254, "thorn"},
130 {732, "tilde"}, {215, "times"}, {8482, "trade"}, {8657, "uArr"},
131 {250, "uacute"}, {8593, "uarr"}, {251, "ucirc"}, {249, "ugrave"},
132 {168, "uml"}, {978, "upsih"}, {965, "upsilon"}, {252, "uuml"},
133 {8472, "weierp"}, {958, "xi"}, {253, "yacute"}, {165, "yen"},
134 {255, "yuml"}, {950, "zeta"}, {8205, "zwj"}, {8204, "zwnj"},
135 };
136
137 // Sorted by |m_uCode|.
138 const XFA_FMHtmlReserveCode kReservesForEncode[] = {
139 {34, "quot"}, {38, "amp"}, {39, "apos"}, {60, "lt"},
140 {62, "gt"}, {160, "nbsp"}, {161, "iexcl"}, {162, "cent"},
141 {163, "pund"}, {164, "current"}, {165, "yen"}, {166, "brvbar"},
142 {167, "sect"}, {168, "uml"}, {169, "copy"}, {170, "ordf"},
143 {171, "laquo"}, {172, "not"}, {173, "shy"}, {174, "reg"},
144 {175, "macr"}, {176, "deg"}, {177, "plusmn"}, {178, "sup2"},
145 {179, "sup3"}, {180, "acute"}, {181, "micro"}, {182, "para"},
146 {183, "middot"}, {184, "cedil"}, {185, "sup1"}, {186, "ordm"},
147 {187, "raquo"}, {188, "frac14"}, {189, "frac12"}, {190, "frac34"},
148 {191, "iquest"}, {192, "Agrave"}, {193, "Aacute"}, {194, "Acirc"},
149 {195, "Atilde"}, {196, "Auml"}, {197, "Aring"}, {198, "AElig"},
150 {199, "Ccedil"}, {200, "Egrave"}, {201, "Eacute"}, {202, "Ecirc"},
151 {203, "Euml"}, {204, "lgrave"}, {205, "lacute"}, {206, "lcirc"},
152 {207, "luml"}, {208, "ETH"}, {209, "Ntilde"}, {210, "Ograve"},
153 {211, "Oacute"}, {212, "Ocirc"}, {213, "Otilde"}, {214, "Ouml"},
154 {215, "times"}, {216, "Oslash"}, {217, "Ugrave"}, {218, "Uacute"},
155 {219, "Ucirc"}, {220, "Uuml"}, {221, "Yacute"}, {222, "THORN"},
156 {223, "szlig"}, {224, "agrave"}, {225, "aacute"}, {226, "acirc"},
157 {227, "atilde"}, {228, "auml"}, {229, "aring"}, {230, "aelig"},
158 {231, "ccedil"}, {232, "egrave"}, {233, "eacute"}, {234, "ecirc"},
159 {235, "euml"}, {236, "igrave"}, {237, "iacute"}, {238, "icirc"},
160 {239, "iuml"}, {240, "eth"}, {241, "ntilde"}, {242, "ograve"},
161 {243, "oacute"}, {244, "ocirc"}, {245, "otilde"}, {246, "ouml"},
162 {247, "divide"}, {248, "oslash"}, {249, "ugrave"}, {250, "uacute"},
163 {251, "ucirc"}, {252, "uuml"}, {253, "yacute"}, {254, "thorn"},
164 {255, "yuml"}, {338, "OElig"}, {339, "oelig"}, {352, "Scaron"},
165 {353, "saron"}, {376, "Yuml"}, {402, "fnof"}, {710, "circ"},
166 {732, "tilde"}, {913, "Alpha"}, {914, "Beta"}, {915, "Gamma"},
167 {916, "Delta"}, {917, "Epsilon"}, {918, "Zeta"}, {919, "Eta"},
168 {920, "Theta"}, {921, "lota"}, {922, "Kappa"}, {923, "Lambda"},
169 {924, "Mu"}, {925, "Nu"}, {926, "Xi"}, {927, "Omicron"},
170 {928, "Pi"}, {929, "Rho"}, {931, "Sigma"}, {932, "Tau"},
171 {933, "Upsilon"}, {934, "Phi"}, {935, "Chi"}, {936, "Psi"},
172 {937, "Omega"}, {945, "alpha"}, {946, "beta"}, {947, "gamma"},
173 {948, "delta"}, {949, "epsilon"}, {950, "zeta"}, {951, "eta"},
174 {952, "theta"}, {953, "iota"}, {954, "kappa"}, {955, "lambda"},
175 {956, "mu"}, {957, "nu"}, {958, "xi"}, {959, "omicron"},
176 {960, "pi"}, {961, "rho"}, {962, "sigmaf"}, {963, "sigma"},
177 {964, "tau"}, {965, "upsilon"}, {966, "phi"}, {967, "chi"},
178 {968, "psi"}, {969, "omega"}, {977, "thetasym"}, {978, "upsih"},
179 {982, "piv"}, {8194, "ensp"}, {8195, "emsp"}, {8201, "thinsp"},
180 {8204, "zwnj"}, {8205, "zwj"}, {8206, "lrm"}, {8207, "rlm"},
181 {8211, "ndash"}, {8212, "mdash"}, {8216, "lsquo"}, {8217, "rsquo"},
182 {8218, "sbquo"}, {8220, "ldquo"}, {8221, "tdquo"}, {8222, "bdquo"},
183 {8224, "dagger"}, {8225, "Dagger"}, {8226, "bull"}, {8230, "hellip"},
184 {8240, "permil"}, {8242, "prime"}, {8249, "lsaquo"}, {8250, "rsaquo"},
185 {8254, "oline"}, {8260, "frasl"}, {8364, "euro"}, {8465, "image"},
186 {8472, "weierp"}, {8476, "real"}, {8482, "trade"}, {8501, "alefsym"},
187 {8592, "larr"}, {8593, "uarr"}, {8594, "rarr"}, {8595, "darr"},
188 {8596, "harr"}, {8629, "crarr"}, {8656, "lArr"}, {8657, "uArr"},
189 {8658, "rArr"}, {8659, "dArr"}, {8660, "hArr"}, {8704, "forall"},
190 {8706, "part"}, {8707, "exist"}, {8709, "empty"}, {8711, "nabla"},
191 {8712, "isin"}, {8713, "notin"}, {8715, "ni"}, {8719, "prod"},
192 {8721, "sum"}, {8722, "minus"}, {8727, "lowast"}, {8730, "radic"},
193 {8733, "prop"}, {8734, "infin"}, {8736, "ang"}, {8743, "and"},
194 {8744, "or"}, {8745, "cap"}, {8746, "cup"}, {8747, "int"},
195 {8756, "there4"}, {8764, "sim"}, {8773, "cong"}, {8776, "asymp"},
196 {8800, "ne"}, {8801, "equiv"}, {8804, "le"}, {8805, "ge"},
197 {8834, "sub"}, {8835, "sup"}, {8836, "nsub"}, {8838, "sube"},
198 {8839, "supe"}, {8853, "oplus"}, {8855, "otimes"}, {8869, "perp"},
199 {8901, "sdot"}, {8968, "lceil"}, {8969, "rceil"}, {8970, "lfloor"},
200 {8971, "rfloor"}, {9001, "lang"}, {9002, "rang"}, {9674, "loz"},
201 {9824, "spades"}, {9827, "clubs"}, {9829, "hearts"}, {9830, "diams"},
202 };
203
204 const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFunctions[] = {
205 {kFuncTag, "Abs", CFXJSE_FormCalcContext::Abs},
206 {kFuncTag, "Avg", CFXJSE_FormCalcContext::Avg},
207 {kFuncTag, "Ceil", CFXJSE_FormCalcContext::Ceil},
208 {kFuncTag, "Count", CFXJSE_FormCalcContext::Count},
209 {kFuncTag, "Floor", CFXJSE_FormCalcContext::Floor},
210 {kFuncTag, "Max", CFXJSE_FormCalcContext::Max},
211 {kFuncTag, "Min", CFXJSE_FormCalcContext::Min},
212 {kFuncTag, "Mod", CFXJSE_FormCalcContext::Mod},
213 {kFuncTag, "Round", CFXJSE_FormCalcContext::Round},
214 {kFuncTag, "Sum", CFXJSE_FormCalcContext::Sum},
215 {kFuncTag, "Date", CFXJSE_FormCalcContext::Date},
216 {kFuncTag, "Date2Num", CFXJSE_FormCalcContext::Date2Num},
217 {kFuncTag, "DateFmt", CFXJSE_FormCalcContext::DateFmt},
218 {kFuncTag, "IsoDate2Num", CFXJSE_FormCalcContext::IsoDate2Num},
219 {kFuncTag, "IsoTime2Num", CFXJSE_FormCalcContext::IsoTime2Num},
220 {kFuncTag, "LocalDateFmt", CFXJSE_FormCalcContext::LocalDateFmt},
221 {kFuncTag, "LocalTimeFmt", CFXJSE_FormCalcContext::LocalTimeFmt},
222 {kFuncTag, "Num2Date", CFXJSE_FormCalcContext::Num2Date},
223 {kFuncTag, "Num2GMTime", CFXJSE_FormCalcContext::Num2GMTime},
224 {kFuncTag, "Num2Time", CFXJSE_FormCalcContext::Num2Time},
225 {kFuncTag, "Time", CFXJSE_FormCalcContext::Time},
226 {kFuncTag, "Time2Num", CFXJSE_FormCalcContext::Time2Num},
227 {kFuncTag, "TimeFmt", CFXJSE_FormCalcContext::TimeFmt},
228 {kFuncTag, "Apr", CFXJSE_FormCalcContext::Apr},
229 {kFuncTag, "Cterm", CFXJSE_FormCalcContext::CTerm},
230 {kFuncTag, "FV", CFXJSE_FormCalcContext::FV},
231 {kFuncTag, "Ipmt", CFXJSE_FormCalcContext::IPmt},
232 {kFuncTag, "NPV", CFXJSE_FormCalcContext::NPV},
233 {kFuncTag, "Pmt", CFXJSE_FormCalcContext::Pmt},
234 {kFuncTag, "PPmt", CFXJSE_FormCalcContext::PPmt},
235 {kFuncTag, "PV", CFXJSE_FormCalcContext::PV},
236 {kFuncTag, "Rate", CFXJSE_FormCalcContext::Rate},
237 {kFuncTag, "Term", CFXJSE_FormCalcContext::Term},
238 {kFuncTag, "Choose", CFXJSE_FormCalcContext::Choose},
239 {kFuncTag, "Exists", CFXJSE_FormCalcContext::Exists},
240 {kFuncTag, "HasValue", CFXJSE_FormCalcContext::HasValue},
241 {kFuncTag, "Oneof", CFXJSE_FormCalcContext::Oneof},
242 {kFuncTag, "Within", CFXJSE_FormCalcContext::Within},
243 {kFuncTag, "If", CFXJSE_FormCalcContext::If},
244 {kFuncTag, "Eval", CFXJSE_FormCalcContext::Eval},
245 {kFuncTag, "Translate", CFXJSE_FormCalcContext::eval_translation},
246 {kFuncTag, "Ref", CFXJSE_FormCalcContext::Ref},
247 {kFuncTag, "UnitType", CFXJSE_FormCalcContext::UnitType},
248 {kFuncTag, "UnitValue", CFXJSE_FormCalcContext::UnitValue},
249 {kFuncTag, "At", CFXJSE_FormCalcContext::At},
250 {kFuncTag, "Concat", CFXJSE_FormCalcContext::Concat},
251 {kFuncTag, "Decode", CFXJSE_FormCalcContext::Decode},
252 {kFuncTag, "Encode", CFXJSE_FormCalcContext::Encode},
253 {kFuncTag, "Format", CFXJSE_FormCalcContext::Format},
254 {kFuncTag, "Left", CFXJSE_FormCalcContext::Left},
255 {kFuncTag, "Len", CFXJSE_FormCalcContext::Len},
256 {kFuncTag, "Lower", CFXJSE_FormCalcContext::Lower},
257 {kFuncTag, "Ltrim", CFXJSE_FormCalcContext::Ltrim},
258 {kFuncTag, "Parse", CFXJSE_FormCalcContext::Parse},
259 {kFuncTag, "Replace", CFXJSE_FormCalcContext::Replace},
260 {kFuncTag, "Right", CFXJSE_FormCalcContext::Right},
261 {kFuncTag, "Rtrim", CFXJSE_FormCalcContext::Rtrim},
262 {kFuncTag, "Space", CFXJSE_FormCalcContext::Space},
263 {kFuncTag, "Str", CFXJSE_FormCalcContext::Str},
264 {kFuncTag, "Stuff", CFXJSE_FormCalcContext::Stuff},
265 {kFuncTag, "Substr", CFXJSE_FormCalcContext::Substr},
266 {kFuncTag, "Uuid", CFXJSE_FormCalcContext::Uuid},
267 {kFuncTag, "Upper", CFXJSE_FormCalcContext::Upper},
268 {kFuncTag, "WordNum", CFXJSE_FormCalcContext::WordNum},
269 {kFuncTag, "Get", CFXJSE_FormCalcContext::Get},
270 {kFuncTag, "Post", CFXJSE_FormCalcContext::Post},
271 {kFuncTag, "Put", CFXJSE_FormCalcContext::Put},
272 {kFuncTag, "pos_op", CFXJSE_FormCalcContext::positive_operator},
273 {kFuncTag, "neg_op", CFXJSE_FormCalcContext::negative_operator},
274 {kFuncTag, "log_or_op", CFXJSE_FormCalcContext::logical_or_operator},
275 {kFuncTag, "log_and_op", CFXJSE_FormCalcContext::logical_and_operator},
276 {kFuncTag, "log_not_op", CFXJSE_FormCalcContext::logical_not_operator},
277 {kFuncTag, "eq_op", CFXJSE_FormCalcContext::equality_operator},
278 {kFuncTag, "neq_op", CFXJSE_FormCalcContext::notequality_operator},
279 {kFuncTag, "lt_op", CFXJSE_FormCalcContext::less_operator},
280 {kFuncTag, "le_op", CFXJSE_FormCalcContext::lessequal_operator},
281 {kFuncTag, "gt_op", CFXJSE_FormCalcContext::greater_operator},
282 {kFuncTag, "ge_op", CFXJSE_FormCalcContext::greaterequal_operator},
283 {kFuncTag, "plus_op", CFXJSE_FormCalcContext::plus_operator},
284 {kFuncTag, "minus_op", CFXJSE_FormCalcContext::minus_operator},
285 {kFuncTag, "mul_op", CFXJSE_FormCalcContext::multiple_operator},
286 {kFuncTag, "div_op", CFXJSE_FormCalcContext::divide_operator},
287 {kFuncTag, "asgn_val_op", CFXJSE_FormCalcContext::assign_value_operator},
288 {kFuncTag, "dot_acc", CFXJSE_FormCalcContext::dot_accessor},
289 {kFuncTag, "dotdot_acc", CFXJSE_FormCalcContext::dotdot_accessor},
290 {kFuncTag, "concat_obj", CFXJSE_FormCalcContext::concat_fm_object},
291 {kFuncTag, "is_obj", CFXJSE_FormCalcContext::is_fm_object},
292 {kFuncTag, "is_ary", CFXJSE_FormCalcContext::is_fm_array},
293 {kFuncTag, "get_val", CFXJSE_FormCalcContext::get_fm_value},
294 {kFuncTag, "get_jsobj", CFXJSE_FormCalcContext::get_fm_jsobj},
295 {kFuncTag, "var_filter", CFXJSE_FormCalcContext::fm_var_filter},
296 };
297
298 const uint8_t kAltTableDate[] = {
299 255, 255, 255, 3, 9, 255, 255, 255, 255, 255, 255,
300 255, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255,
301 255, 255, 1, 255, 255, 255, 255, 255, 255, 255, 255,
302 };
303 static_assert(std::size(kAltTableDate) == L'a' - L'A' + 1,
304 "Invalid kAltTableDate size.");
305
306 const uint8_t kAltTableTime[] = {
307 14, 255, 255, 3, 9, 255, 255, 15, 255, 255, 255,
308 255, 6, 255, 255, 255, 255, 255, 7, 255, 255, 255,
309 255, 255, 1, 17, 255, 255, 255, 255, 255, 255, 255,
310 };
311 static_assert(std::size(kAltTableTime) == L'a' - L'A' + 1,
312 "Invalid kAltTableTime size.");
313
AlternateDateTimeSymbols(WideString * pPattern,const WideString & wsAltSymbols,bool bIsDate)314 void AlternateDateTimeSymbols(WideString* pPattern,
315 const WideString& wsAltSymbols,
316 bool bIsDate) {
317 const uint8_t* pAltTable = bIsDate ? kAltTableDate : kAltTableTime;
318 int32_t nLength = pPattern->GetLength();
319 bool bInConstRange = false;
320 bool bEscape = false;
321 int32_t i = 0;
322 while (i < nLength) {
323 wchar_t wc = (*pPattern)[i];
324 if (wc == L'\'') {
325 bInConstRange = !bInConstRange;
326 if (bEscape) {
327 i++;
328 } else {
329 pPattern->Delete(i);
330 nLength--;
331 }
332 bEscape = !bEscape;
333 continue;
334 }
335 if (!bInConstRange && wc >= L'A' && wc <= L'a') {
336 uint8_t nAlt = pAltTable[wc - L'A'];
337 if (nAlt != 255)
338 pPattern->SetAt(i, wsAltSymbols[nAlt]);
339 }
340 i++;
341 bEscape = false;
342 }
343 }
344
PatternStringType(ByteStringView bsPattern)345 std::pair<bool, CXFA_LocaleValue::ValueType> PatternStringType(
346 ByteStringView bsPattern) {
347 WideString wsPattern = WideString::FromUTF8(bsPattern);
348 if (L"datetime" == wsPattern.First(8))
349 return {true, CXFA_LocaleValue::ValueType::kDateTime};
350 if (L"date" == wsPattern.First(4)) {
351 auto pos = wsPattern.Find(L"time");
352 if (pos.has_value() && pos.value() != 0)
353 return {true, CXFA_LocaleValue::ValueType::kDateTime};
354 return {true, CXFA_LocaleValue::ValueType::kDate};
355 }
356 if (L"time" == wsPattern.First(4))
357 return {true, CXFA_LocaleValue::ValueType::kTime};
358 if (L"text" == wsPattern.First(4))
359 return {true, CXFA_LocaleValue::ValueType::kText};
360 if (L"num" == wsPattern.First(3)) {
361 if (L"integer" == wsPattern.Substr(4, 7))
362 return {true, CXFA_LocaleValue::ValueType::kInteger};
363 if (L"decimal" == wsPattern.Substr(4, 7))
364 return {true, CXFA_LocaleValue::ValueType::kDecimal};
365 if (L"currency" == wsPattern.Substr(4, 8))
366 return {true, CXFA_LocaleValue::ValueType::kFloat};
367 if (L"percent" == wsPattern.Substr(4, 7))
368 return {true, CXFA_LocaleValue::ValueType::kFloat};
369 return {true, CXFA_LocaleValue::ValueType::kFloat};
370 }
371
372 CXFA_LocaleValue::ValueType type = CXFA_LocaleValue::ValueType::kNull;
373 wsPattern.MakeLower();
374 const wchar_t* pData = wsPattern.c_str();
375 int32_t iLength = wsPattern.GetLength();
376 int32_t iIndex = 0;
377 bool bSingleQuotation = false;
378 while (iIndex < iLength) {
379 wchar_t wsPatternChar = pData[iIndex];
380 if (wsPatternChar == 0x27) {
381 bSingleQuotation = !bSingleQuotation;
382 iIndex++;
383 continue;
384 }
385 if (bSingleQuotation) {
386 iIndex++;
387 continue;
388 }
389
390 if (wsPatternChar == 'h' || wsPatternChar == 'k')
391 return {false, CXFA_LocaleValue::ValueType::kTime};
392 if (wsPatternChar == 'x' || wsPatternChar == 'o' || wsPatternChar == '0')
393 return {false, CXFA_LocaleValue::ValueType::kText};
394 if (wsPatternChar == 'v' || wsPatternChar == '8' || wsPatternChar == '$')
395 return {false, CXFA_LocaleValue::ValueType::kFloat};
396 if (wsPatternChar == 'y' || wsPatternChar == 'j') {
397 iIndex++;
398 wchar_t timePatternChar;
399 while (iIndex < iLength) {
400 timePatternChar = pData[iIndex];
401 if (timePatternChar == 0x27) {
402 bSingleQuotation = !bSingleQuotation;
403 iIndex++;
404 continue;
405 }
406 if (!bSingleQuotation && timePatternChar == 't')
407 return {false, CXFA_LocaleValue::ValueType::kDateTime};
408 iIndex++;
409 }
410 return {false, CXFA_LocaleValue::ValueType::kDate};
411 }
412
413 if (wsPatternChar == 'a') {
414 type = CXFA_LocaleValue::ValueType::kText;
415 } else if (wsPatternChar == 'z' || wsPatternChar == 's' ||
416 wsPatternChar == 'e' || wsPatternChar == ',' ||
417 wsPatternChar == '.') {
418 type = CXFA_LocaleValue::ValueType::kFloat;
419 }
420 iIndex++;
421 }
422 return {false, type};
423 }
424
ToFormCalcContext(CFXJSE_HostObject * pHostObj)425 CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_HostObject* pHostObj) {
426 return pHostObj ? pHostObj->AsFormCalcContext() : nullptr;
427 }
428
LocaleFromString(CXFA_Document * pDoc,CXFA_LocaleMgr * pMgr,ByteStringView bsLocale)429 GCedLocaleIface* LocaleFromString(CXFA_Document* pDoc,
430 CXFA_LocaleMgr* pMgr,
431 ByteStringView bsLocale) {
432 if (!bsLocale.IsEmpty())
433 return pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale));
434
435 CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
436 return pThisNode->GetLocale();
437 }
438
FormatFromString(LocaleIface * pLocale,ByteStringView bsFormat)439 WideString FormatFromString(LocaleIface* pLocale, ByteStringView bsFormat) {
440 if (!bsFormat.IsEmpty())
441 return WideString::FromUTF8(bsFormat);
442
443 return pLocale->GetDatePattern(LocaleIface::DateTimeSubcategory::kDefault);
444 }
445
SubCategoryFromInt(int32_t iStyle)446 LocaleIface::DateTimeSubcategory SubCategoryFromInt(int32_t iStyle) {
447 switch (iStyle) {
448 case 1:
449 return LocaleIface::DateTimeSubcategory::kShort;
450 case 3:
451 return LocaleIface::DateTimeSubcategory::kLong;
452 case 4:
453 return LocaleIface::DateTimeSubcategory::kFull;
454 case 0:
455 case 2:
456 default:
457 return LocaleIface::DateTimeSubcategory::kMedium;
458 }
459 }
460
GetLocalDateTimeFormat(CXFA_Document * pDoc,int32_t iStyle,ByteStringView bsLocale,bool bStandard,bool bIsDate)461 ByteString GetLocalDateTimeFormat(CXFA_Document* pDoc,
462 int32_t iStyle,
463 ByteStringView bsLocale,
464 bool bStandard,
465 bool bIsDate) {
466 CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
467 LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
468 if (!pLocale)
469 return ByteString();
470
471 LocaleIface::DateTimeSubcategory category = SubCategoryFromInt(iStyle);
472 WideString wsLocal = bIsDate ? pLocale->GetDatePattern(category)
473 : pLocale->GetTimePattern(category);
474 if (!bStandard)
475 AlternateDateTimeSymbols(&wsLocal, pLocale->GetDateTimeSymbols(), bIsDate);
476 return wsLocal.ToUTF8();
477 }
478
IsWhitespace(char c)479 bool IsWhitespace(char c) {
480 return c == 0x20 || c == 0x09 || c == 0x0B || c == 0x0C || c == 0x0A ||
481 c == 0x0D;
482 }
483
IsPartOfNumber(char ch)484 bool IsPartOfNumber(char ch) {
485 return isdigit(ch) || ch == '-' || ch == '.';
486 }
487
IsPartOfNumberW(wchar_t ch)488 bool IsPartOfNumberW(wchar_t ch) {
489 return FXSYS_IsDecimalDigit(ch) || ch == L'-' || ch == L'.';
490 }
491
GUIDString(bool bSeparator)492 ByteString GUIDString(bool bSeparator) {
493 uint8_t data[16];
494 FX_Random_GenerateMT(reinterpret_cast<uint32_t*>(data), 4);
495 data[6] = (data[6] & 0x0F) | 0x40;
496
497 ByteString bsGUID;
498 {
499 // Span's lifetime must end before ReleaseBuffer() below.
500 pdfium::span<char> pBuf = bsGUID.GetBuffer(40);
501 size_t out_index = 0;
502 for (size_t i = 0; i < 16; ++i, out_index += 2) {
503 if (bSeparator && (i == 4 || i == 6 || i == 8 || i == 10))
504 pBuf[out_index++] = L'-';
505
506 FXSYS_IntToTwoHexChars(data[i], &pBuf[out_index]);
507 }
508 }
509 bsGUID.ReleaseBuffer(bSeparator ? 36 : 32);
510 return bsGUID;
511 }
512
IsIsoDateFormat(pdfium::span<const char> pData,int32_t * pStyle,int32_t * pYear,int32_t * pMonth,int32_t * pDay)513 bool IsIsoDateFormat(pdfium::span<const char> pData,
514 int32_t* pStyle,
515 int32_t* pYear,
516 int32_t* pMonth,
517 int32_t* pDay) {
518 int32_t& iStyle = *pStyle;
519 int32_t& iYear = *pYear;
520 int32_t& iMonth = *pMonth;
521 int32_t& iDay = *pDay;
522
523 iYear = 0;
524 iMonth = 1;
525 iDay = 1;
526
527 if (pData.size() < 4)
528 return false;
529
530 char szYear[5];
531 szYear[4] = '\0';
532 for (int32_t i = 0; i < 4; ++i) {
533 if (!isdigit(pData[i]))
534 return false;
535
536 szYear[i] = pData[i];
537 }
538 iYear = FXSYS_atoi(szYear);
539 iStyle = 0;
540 if (pData.size() == 4)
541 return true;
542
543 iStyle = pData[4] == '-' ? 1 : 0;
544
545 size_t iPosOff = iStyle == 0 ? 4 : 5;
546 if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1]))
547 return false;
548
549 char szBuffer[3] = {};
550 szBuffer[0] = pData[iPosOff];
551 szBuffer[1] = pData[iPosOff + 1];
552 iMonth = FXSYS_atoi(szBuffer);
553 if (iMonth > 12 || iMonth < 1)
554 return false;
555
556 if (iStyle == 0) {
557 iPosOff += 2;
558 if (pData.size() == 6)
559 return true;
560 } else {
561 iPosOff += 3;
562 if (pData.size() == 7)
563 return true;
564 }
565 if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1]))
566 return false;
567
568 szBuffer[0] = pData[iPosOff];
569 szBuffer[1] = pData[iPosOff + 1];
570 iDay = FXSYS_atoi(szBuffer);
571 if (iPosOff + 2 < pData.size())
572 return false;
573
574 if (iMonth == 2) {
575 bool bIsLeap = (!(iYear % 4) && (iYear % 100)) || !(iYear % 400);
576 return iDay <= (bIsLeap ? 29 : 28);
577 }
578
579 if (iMonth < 8)
580 return iDay <= (iMonth % 2 == 0 ? 30 : 31);
581 return iDay <= (iMonth % 2 == 0 ? 31 : 30);
582 }
583
IsIsoTimeFormat(pdfium::span<const char> pData,int32_t * pHour,int32_t * pMinute,int32_t * pSecond,int32_t * pMilliSecond,int32_t * pZoneHour,int32_t * pZoneMinute)584 bool IsIsoTimeFormat(pdfium::span<const char> pData,
585 int32_t* pHour,
586 int32_t* pMinute,
587 int32_t* pSecond,
588 int32_t* pMilliSecond,
589 int32_t* pZoneHour,
590 int32_t* pZoneMinute) {
591 int32_t& iHour = *pHour;
592 int32_t& iMinute = *pMinute;
593 int32_t& iSecond = *pSecond;
594 int32_t& iMilliSecond = *pMilliSecond;
595 int32_t& iZoneHour = *pZoneHour;
596 int32_t& iZoneMinute = *pZoneMinute;
597
598 iHour = 0;
599 iMinute = 0;
600 iSecond = 0;
601 iMilliSecond = 0;
602 iZoneHour = 0;
603 iZoneMinute = 0;
604
605 if (pData.empty())
606 return false;
607
608 size_t iZone = 0;
609 size_t i = 0;
610 while (i < pData.size()) {
611 if (!isdigit(pData[i]) && pData[i] != ':') {
612 iZone = i;
613 break;
614 }
615 ++i;
616 }
617 if (i == pData.size())
618 iZone = pData.size();
619
620 char szBuffer[3] = {};
621 size_t iPos = 0;
622 size_t iIndex = 0;
623 while (iIndex < iZone) {
624 if (!isdigit(pData[iIndex]))
625 return false;
626
627 szBuffer[0] = pData[iIndex];
628 if (!isdigit(pData[iIndex + 1]))
629 return false;
630
631 szBuffer[1] = pData[iIndex + 1];
632 if (FXSYS_atoi(szBuffer) > 60)
633 return false;
634
635 if (pData[2] == ':') {
636 if (iPos == 0) {
637 iHour = FXSYS_atoi(szBuffer);
638 ++iPos;
639 } else if (iPos == 1) {
640 iMinute = FXSYS_atoi(szBuffer);
641 ++iPos;
642 } else {
643 iSecond = FXSYS_atoi(szBuffer);
644 }
645 iIndex += 3;
646 } else {
647 if (iPos == 0) {
648 iHour = FXSYS_atoi(szBuffer);
649 ++iPos;
650 } else if (iPos == 1) {
651 iMinute = FXSYS_atoi(szBuffer);
652 ++iPos;
653 } else if (iPos == 2) {
654 iSecond = FXSYS_atoi(szBuffer);
655 ++iPos;
656 }
657 iIndex += 2;
658 }
659 }
660
661 if (iIndex < pData.size() && pData[iIndex] == '.') {
662 constexpr int kSubSecondLength = 3;
663 if (iIndex + kSubSecondLength >= pData.size())
664 return false;
665
666 ++iIndex;
667 char szMilliSeconds[kSubSecondLength + 1];
668 for (int j = 0; j < kSubSecondLength; ++j) {
669 char c = pData[iIndex + j];
670 if (!isdigit(c))
671 return false;
672 szMilliSeconds[j] = c;
673 }
674 szMilliSeconds[kSubSecondLength] = '\0';
675
676 iMilliSecond = FXSYS_atoi(szMilliSeconds);
677 if (iMilliSecond > 100) {
678 iMilliSecond = 0;
679 return false;
680 }
681 iIndex += kSubSecondLength;
682 }
683
684 if (iIndex < pData.size() && FXSYS_towlower(pData[iIndex]) == 'z')
685 return true;
686
687 int32_t iSign = 1;
688 if (iIndex < pData.size()) {
689 if (pData[iIndex] == '+') {
690 ++iIndex;
691 } else if (pData[iIndex] == '-') {
692 iSign = -1;
693 ++iIndex;
694 }
695 }
696 iPos = 0;
697 while (iIndex < pData.size()) {
698 if (!isdigit(pData[iIndex]))
699 return false;
700
701 szBuffer[0] = pData[iIndex];
702 if (!isdigit(pData[iIndex + 1]))
703 return false;
704
705 szBuffer[1] = pData[iIndex + 1];
706 if (FXSYS_atoi(szBuffer) > 60)
707 return false;
708
709 if (pData[2] == ':') {
710 if (iPos == 0) {
711 iZoneHour = FXSYS_atoi(szBuffer);
712 } else if (iPos == 1) {
713 iZoneMinute = FXSYS_atoi(szBuffer);
714 }
715 iIndex += 3;
716 } else {
717 if (!iPos) {
718 iZoneHour = FXSYS_atoi(szBuffer);
719 ++iPos;
720 } else if (iPos == 1) {
721 iZoneMinute = FXSYS_atoi(szBuffer);
722 ++iPos;
723 }
724 iIndex += 2;
725 }
726 }
727 if (iIndex < pData.size())
728 return false;
729
730 iZoneHour *= iSign;
731 return true;
732 }
733
IsIsoDateTimeFormat(pdfium::span<const char> pData,int32_t * pYear,int32_t * pMonth,int32_t * pDay,int32_t * pHour,int32_t * pMinute,int32_t * pSecond,int32_t * pMilliSecond,int32_t * pZoneHour,int32_t * pZoneMinute)734 bool IsIsoDateTimeFormat(pdfium::span<const char> pData,
735 int32_t* pYear,
736 int32_t* pMonth,
737 int32_t* pDay,
738 int32_t* pHour,
739 int32_t* pMinute,
740 int32_t* pSecond,
741 int32_t* pMilliSecond,
742 int32_t* pZoneHour,
743 int32_t* pZoneMinute) {
744 *pYear = 0;
745 *pMonth = 0;
746 *pDay = 0;
747 *pHour = 0;
748 *pMinute = 0;
749 *pSecond = 0;
750
751 size_t iIndex = 0;
752 while (iIndex < pData.size()) {
753 if (pData[iIndex] == 'T' || pData[iIndex] == 't')
754 break;
755 ++iIndex;
756 }
757 if (iIndex == pData.size() || (iIndex != 8 && iIndex != 10))
758 return false;
759
760 pdfium::span<const char> pDateSpan = pData.subspan(0, iIndex);
761 pdfium::span<const char> pTimeSpan = pData.subspan(iIndex + 1);
762
763 int32_t iStyle = -1;
764 return IsIsoDateFormat(pDateSpan, &iStyle, pYear, pMonth, pDay) &&
765 IsIsoTimeFormat(pTimeSpan, pHour, pMinute, pSecond, pMilliSecond,
766 pZoneHour, pZoneMinute);
767 }
768
DateString2Num(ByteStringView bsDate)769 int32_t DateString2Num(ByteStringView bsDate) {
770 int32_t iLength = bsDate.GetLength();
771 int32_t iYear = 0;
772 int32_t iMonth = 0;
773 int32_t iDay = 0;
774 if (iLength <= 10) {
775 int32_t iStyle = -1;
776 if (!IsIsoDateFormat(bsDate.span(), &iStyle, &iYear, &iMonth, &iDay))
777 return 0;
778 } else {
779 int32_t iHour = 0;
780 int32_t iMinute = 0;
781 int32_t iSecond = 0;
782 int32_t iMilliSecond = 0;
783 int32_t iZoneHour = 0;
784 int32_t iZoneMinute = 0;
785 if (!IsIsoDateTimeFormat(bsDate.span(), &iYear, &iMonth, &iDay, &iHour,
786 &iMinute, &iSecond, &iMilliSecond, &iZoneHour,
787 &iZoneMinute)) {
788 return 0;
789 }
790 }
791
792 float dDays = 0;
793 int32_t i = 1;
794 if (iYear < 1900)
795 return 0;
796
797 while (iYear - i >= 1900) {
798 dDays +=
799 ((!((iYear - i) % 4) && ((iYear - i) % 100)) || !((iYear - i) % 400))
800 ? 366
801 : 365;
802 ++i;
803 }
804 i = 1;
805 while (i < iMonth) {
806 if (i == 2)
807 dDays += ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) ? 29 : 28;
808 else if (i <= 7)
809 dDays += (i % 2 == 0) ? 30 : 31;
810 else
811 dDays += (i % 2 == 0) ? 31 : 30;
812
813 ++i;
814 }
815 i = 0;
816 while (iDay - i > 0) {
817 ++dDays;
818 ++i;
819 }
820 return static_cast<int32_t>(dDays);
821 }
822
GetLocalTimeZone(int32_t * pHour,int32_t * pMin,int32_t * pSec)823 void GetLocalTimeZone(int32_t* pHour, int32_t* pMin, int32_t* pSec) {
824 time_t now;
825 FXSYS_time(&now);
826
827 struct tm* pGmt = gmtime(&now);
828 struct tm* pLocal = FXSYS_localtime(&now);
829 *pHour = pLocal->tm_hour - pGmt->tm_hour;
830 *pMin = pLocal->tm_min - pGmt->tm_min;
831 *pSec = pLocal->tm_sec - pGmt->tm_sec;
832 }
833
HTMLSTR2Code(const WideString & pData,uint32_t * iCode)834 bool HTMLSTR2Code(const WideString& pData, uint32_t* iCode) {
835 auto cmpFunc = [](const XFA_FMHtmlReserveCode& iter, ByteStringView val) {
836 return strcmp(val.unterminated_c_str(), iter.m_htmlReserve) > 0;
837 };
838 if (!pData.IsASCII())
839 return false;
840 ByteString temp = pData.ToASCII();
841 const XFA_FMHtmlReserveCode* result = std::lower_bound(
842 std::begin(kReservesForDecode), std::end(kReservesForDecode),
843 temp.AsStringView(), cmpFunc);
844 if (result != std::end(kReservesForDecode) &&
845 !strcmp(temp.c_str(), result->m_htmlReserve)) {
846 *iCode = result->m_uCode;
847 return true;
848 }
849 return false;
850 }
851
HTMLCode2STR(uint32_t iCode,WideString * wsHTMLReserve)852 bool HTMLCode2STR(uint32_t iCode, WideString* wsHTMLReserve) {
853 auto cmpFunc = [](const XFA_FMHtmlReserveCode iter, const uint32_t& val) {
854 return iter.m_uCode < val;
855 };
856 const XFA_FMHtmlReserveCode* result =
857 std::lower_bound(std::begin(kReservesForEncode),
858 std::end(kReservesForEncode), iCode, cmpFunc);
859 if (result != std::end(kReservesForEncode) && result->m_uCode == iCode) {
860 *wsHTMLReserve = WideString::FromASCII(result->m_htmlReserve);
861 return true;
862 }
863 return false;
864 }
865
DecodeURL(const WideString & wsURL)866 WideString DecodeURL(const WideString& wsURL) {
867 const wchar_t* pData = wsURL.c_str();
868 size_t iLen = wsURL.GetLength();
869 WideTextBuffer wsResultBuf;
870 for (size_t i = 0; i < iLen; ++i) {
871 wchar_t ch = pData[i];
872 if ('%' != ch) {
873 wsResultBuf.AppendChar(ch);
874 continue;
875 }
876
877 wchar_t chTemp = 0;
878 int32_t iCount = 0;
879 while (iCount < 2) {
880 if (++i >= iLen)
881 break;
882 chTemp *= 16;
883 ch = pData[i];
884 if (!FXSYS_IsWideHexDigit(ch))
885 return WideString();
886 chTemp += FXSYS_WideHexCharToInt(ch);
887 ++iCount;
888 }
889 wsResultBuf.AppendChar(chTemp);
890 }
891 return wsResultBuf.MakeString();
892 }
893
DecodeMLInternal(const WideString & wsHTML,bool bIsHTML)894 WideString DecodeMLInternal(const WideString& wsHTML, bool bIsHTML) {
895 const wchar_t* pData = wsHTML.c_str();
896 size_t iLen = wsHTML.GetLength();
897 WideTextBuffer wsResultBuf;
898 for (size_t i = 0; i < iLen; ++i) {
899 wchar_t ch = pData[i];
900 if (ch != '&') {
901 wsResultBuf.AppendChar(ch);
902 continue;
903 }
904
905 if (++i >= iLen)
906 break;
907 ch = pData[i];
908 if (ch == '#') {
909 if (++i >= iLen)
910 break;
911 ch = pData[i];
912 if (ch != 'x' && ch != 'X')
913 return WideString();
914 if (++i >= iLen)
915 break;
916 ch = pData[i];
917 uint32_t iCode = 0;
918 while (ch != ';' && i < iLen) {
919 iCode *= 16;
920 if (!FXSYS_IsWideHexDigit(ch))
921 return WideString();
922 iCode += FXSYS_WideHexCharToInt(ch);
923 if (++i >= iLen)
924 break;
925 ch = pData[i];
926 }
927 wsResultBuf.AppendChar(iCode);
928 continue;
929 }
930
931 wchar_t szBuffer[9];
932 size_t iStrIndex = 0;
933 while (ch != ';' && i < iLen) {
934 if (iStrIndex < 8)
935 szBuffer[iStrIndex++] = ch;
936 if (++i >= iLen)
937 break;
938 ch = pData[i];
939 }
940 szBuffer[iStrIndex] = 0;
941 if (bIsHTML) {
942 uint32_t iData = 0;
943 if (HTMLSTR2Code(szBuffer, &iData))
944 wsResultBuf.AppendChar((wchar_t)iData);
945 } else {
946 if (wcscmp(szBuffer, L"quot") == 0)
947 wsResultBuf.AppendChar('"');
948 else if (wcscmp(szBuffer, L"amp") == 0)
949 wsResultBuf.AppendChar('&');
950 else if (wcscmp(szBuffer, L"apos") == 0)
951 wsResultBuf.AppendChar('\'');
952 else if (wcscmp(szBuffer, L"lt") == 0)
953 wsResultBuf.AppendChar('<');
954 else if (wcscmp(szBuffer, L"gt") == 0)
955 wsResultBuf.AppendChar('>');
956 }
957 }
958 return wsResultBuf.MakeString();
959 }
960
DecodeHTML(const WideString & wsHTML)961 WideString DecodeHTML(const WideString& wsHTML) {
962 return DecodeMLInternal(wsHTML, true);
963 }
964
DecodeXML(const WideString & wsXML)965 WideString DecodeXML(const WideString& wsXML) {
966 return DecodeMLInternal(wsXML, false);
967 }
968
EncodeURL(const ByteString & bsURL)969 WideString EncodeURL(const ByteString& bsURL) {
970 static const wchar_t kStrUnsafe[] = {' ', '<', '>', '"', '#', '%', '{', '}',
971 '|', '\\', '^', '~', '[', ']', '`'};
972 static const wchar_t kStrReserved[] = {';', '/', '?', ':', '@', '=', '&'};
973 static const wchar_t kStrSpecial[] = {'$', '-', '+', '!', '*',
974 '\'', '(', ')', ','};
975
976 WideString wsURL = WideString::FromUTF8(bsURL.AsStringView());
977 WideTextBuffer wsResultBuf;
978 wchar_t szEncode[4];
979 szEncode[0] = '%';
980 szEncode[3] = 0;
981 for (wchar_t ch : wsURL) {
982 size_t i = 0;
983 size_t iCount = std::size(kStrUnsafe);
984 while (i < iCount) {
985 if (ch == kStrUnsafe[i]) {
986 int32_t iIndex = ch / 16;
987 szEncode[1] = kStrCode[iIndex];
988 szEncode[2] = kStrCode[ch - iIndex * 16];
989 wsResultBuf << szEncode;
990 break;
991 }
992 ++i;
993 }
994 if (i < iCount)
995 continue;
996
997 i = 0;
998 iCount = std::size(kStrReserved);
999 while (i < iCount) {
1000 if (ch == kStrReserved[i]) {
1001 int32_t iIndex = ch / 16;
1002 szEncode[1] = kStrCode[iIndex];
1003 szEncode[2] = kStrCode[ch - iIndex * 16];
1004 wsResultBuf << szEncode;
1005 break;
1006 }
1007 ++i;
1008 }
1009 if (i < iCount)
1010 continue;
1011
1012 i = 0;
1013 iCount = std::size(kStrSpecial);
1014 while (i < iCount) {
1015 if (ch == kStrSpecial[i]) {
1016 wsResultBuf.AppendChar(ch);
1017 break;
1018 }
1019 ++i;
1020 }
1021 if (i < iCount)
1022 continue;
1023
1024 if ((ch >= 0x80 && ch <= 0xff) || ch <= 0x1f || ch == 0x7f) {
1025 int32_t iIndex = ch / 16;
1026 szEncode[1] = kStrCode[iIndex];
1027 szEncode[2] = kStrCode[ch - iIndex * 16];
1028 wsResultBuf << szEncode;
1029 } else if (ch >= 0x20 && ch <= 0x7e) {
1030 wsResultBuf.AppendChar(ch);
1031 } else {
1032 const wchar_t iRadix = 16;
1033 WideString wsBuffer;
1034 while (ch >= iRadix) {
1035 wchar_t tmp = kStrCode[ch % iRadix];
1036 ch /= iRadix;
1037 wsBuffer += tmp;
1038 }
1039 wsBuffer += kStrCode[ch];
1040 int32_t iLen = wsBuffer.GetLength();
1041 if (iLen < 2)
1042 break;
1043
1044 int32_t iIndex = 0;
1045 if (iLen % 2 != 0) {
1046 szEncode[1] = '0';
1047 szEncode[2] = wsBuffer[iLen - 1];
1048 iIndex = iLen - 2;
1049 } else {
1050 szEncode[1] = wsBuffer[iLen - 1];
1051 szEncode[2] = wsBuffer[iLen - 2];
1052 iIndex = iLen - 3;
1053 }
1054 wsResultBuf << szEncode;
1055 while (iIndex > 0) {
1056 szEncode[1] = wsBuffer[iIndex];
1057 szEncode[2] = wsBuffer[iIndex - 1];
1058 iIndex -= 2;
1059 wsResultBuf << szEncode;
1060 }
1061 }
1062 }
1063 return wsResultBuf.MakeString();
1064 }
1065
EncodeHTML(const ByteString & bsHTML)1066 WideString EncodeHTML(const ByteString& bsHTML) {
1067 WideString wsHTML = WideString::FromUTF8(bsHTML.AsStringView());
1068 wchar_t szEncode[9];
1069 szEncode[0] = '&';
1070 szEncode[1] = '#';
1071 szEncode[2] = 'x';
1072 WideTextBuffer wsResultBuf;
1073 for (uint32_t ch : wsHTML) {
1074 WideString htmlReserve;
1075 if (HTMLCode2STR(ch, &htmlReserve)) {
1076 wsResultBuf.AppendChar(L'&');
1077 wsResultBuf << htmlReserve;
1078 wsResultBuf.AppendChar(L';');
1079 } else if (ch >= 32 && ch <= 126) {
1080 wsResultBuf.AppendChar(static_cast<wchar_t>(ch));
1081 } else if (ch < 256) {
1082 int32_t iIndex = ch / 16;
1083 szEncode[3] = kStrCode[iIndex];
1084 szEncode[4] = kStrCode[ch - iIndex * 16];
1085 szEncode[5] = ';';
1086 szEncode[6] = 0;
1087 wsResultBuf << szEncode;
1088 } else if (ch < 65536) {
1089 int32_t iBigByte = ch / 256;
1090 int32_t iLittleByte = ch % 256;
1091 szEncode[3] = kStrCode[iBigByte / 16];
1092 szEncode[4] = kStrCode[iBigByte % 16];
1093 szEncode[5] = kStrCode[iLittleByte / 16];
1094 szEncode[6] = kStrCode[iLittleByte % 16];
1095 szEncode[7] = ';';
1096 szEncode[8] = 0;
1097 wsResultBuf << szEncode;
1098 } else {
1099 // TODO(tsepez): Handle codepoint not in BMP.
1100 }
1101 }
1102 return wsResultBuf.MakeString();
1103 }
1104
EncodeXML(const ByteString & bsXML)1105 WideString EncodeXML(const ByteString& bsXML) {
1106 WideString wsXML = WideString::FromUTF8(bsXML.AsStringView());
1107 WideTextBuffer wsResultBuf;
1108 wchar_t szEncode[9];
1109 szEncode[0] = '&';
1110 szEncode[1] = '#';
1111 szEncode[2] = 'x';
1112 for (uint32_t ch : wsXML) {
1113 switch (ch) {
1114 case '"':
1115 wsResultBuf.AppendChar('&');
1116 wsResultBuf << WideStringView(L"quot");
1117 wsResultBuf.AppendChar(';');
1118 break;
1119 case '&':
1120 wsResultBuf.AppendChar('&');
1121 wsResultBuf << WideStringView(L"amp");
1122 wsResultBuf.AppendChar(';');
1123 break;
1124 case '\'':
1125 wsResultBuf.AppendChar('&');
1126 wsResultBuf << WideStringView(L"apos");
1127 wsResultBuf.AppendChar(';');
1128 break;
1129 case '<':
1130 wsResultBuf.AppendChar('&');
1131 wsResultBuf << WideStringView(L"lt");
1132 wsResultBuf.AppendChar(';');
1133 break;
1134 case '>':
1135 wsResultBuf.AppendChar('&');
1136 wsResultBuf << WideStringView(L"gt");
1137 wsResultBuf.AppendChar(';');
1138 break;
1139 default: {
1140 if (ch >= 32 && ch <= 126) {
1141 wsResultBuf.AppendChar(static_cast<wchar_t>(ch));
1142 } else if (ch < 256) {
1143 int32_t iIndex = ch / 16;
1144 szEncode[3] = kStrCode[iIndex];
1145 szEncode[4] = kStrCode[ch - iIndex * 16];
1146 szEncode[5] = ';';
1147 szEncode[6] = 0;
1148 wsResultBuf << szEncode;
1149 } else if (ch < 65536) {
1150 int32_t iBigByte = ch / 256;
1151 int32_t iLittleByte = ch % 256;
1152 szEncode[3] = kStrCode[iBigByte / 16];
1153 szEncode[4] = kStrCode[iBigByte % 16];
1154 szEncode[5] = kStrCode[iLittleByte / 16];
1155 szEncode[6] = kStrCode[iLittleByte % 16];
1156 szEncode[7] = ';';
1157 szEncode[8] = 0;
1158 wsResultBuf << szEncode;
1159 } else {
1160 // TODO(tsepez): Handle codepoint not in BMP.
1161 }
1162 break;
1163 }
1164 }
1165 }
1166 return wsResultBuf.MakeString();
1167 }
1168
TrillionUS(ByteStringView bsData)1169 ByteString TrillionUS(ByteStringView bsData) {
1170 static const char kUnits[][6] = {"zero", "one", "two", "three", "four",
1171 "five", "six", "seven", "eight", "nine"};
1172 static const char kCapUnits[][6] = {"Zero", "One", "Two", "Three", "Four",
1173 "Five", "Six", "Seven", "Eight", "Nine"};
1174 static const char kTens[][10] = {
1175 "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
1176 "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
1177 static const char kLastTens[][8] = {"Twenty", "Thirty", "Forty", "Fifty",
1178 "Sixty", "Seventy", "Eighty", "Ninety"};
1179 static const char kComm[][11] = {" Hundred ", " Thousand ", " Million ",
1180 " Billion ", "Trillion"};
1181 const char* pData = bsData.unterminated_c_str();
1182 int32_t iLength = bsData.GetLength();
1183 int32_t iComm = 0;
1184 if (iLength > 12)
1185 iComm = 4;
1186 else if (iLength > 9)
1187 iComm = 3;
1188 else if (iLength > 6)
1189 iComm = 2;
1190 else if (iLength > 3)
1191 iComm = 1;
1192
1193 int32_t iFirstCount = iLength % 3;
1194 if (iFirstCount == 0)
1195 iFirstCount = 3;
1196
1197 ByteString strBuf;
1198 int32_t iIndex = 0;
1199 if (iFirstCount == 3) {
1200 if (pData[iIndex] != '0') {
1201 strBuf += kCapUnits[pData[iIndex] - '0'];
1202 strBuf += kComm[0];
1203 }
1204 if (pData[iIndex + 1] == '0') {
1205 strBuf += kCapUnits[pData[iIndex + 2] - '0'];
1206 } else {
1207 if (pData[iIndex + 1] > '1') {
1208 strBuf += kLastTens[pData[iIndex + 1] - '2'];
1209 strBuf += "-";
1210 strBuf += kUnits[pData[iIndex + 2] - '0'];
1211 } else if (pData[iIndex + 1] == '1') {
1212 strBuf += kTens[pData[iIndex + 2] - '0'];
1213 } else if (pData[iIndex + 1] == '0') {
1214 strBuf += kCapUnits[pData[iIndex + 2] - '0'];
1215 }
1216 }
1217 iIndex += 3;
1218 } else if (iFirstCount == 2) {
1219 if (pData[iIndex] == '0') {
1220 strBuf += kCapUnits[pData[iIndex + 1] - '0'];
1221 } else {
1222 if (pData[iIndex] > '1') {
1223 strBuf += kLastTens[pData[iIndex] - '2'];
1224 strBuf += "-";
1225 strBuf += kUnits[pData[iIndex + 1] - '0'];
1226 } else if (pData[iIndex] == '1') {
1227 strBuf += kTens[pData[iIndex + 1] - '0'];
1228 } else if (pData[iIndex] == '0') {
1229 strBuf += kCapUnits[pData[iIndex + 1] - '0'];
1230 }
1231 }
1232 iIndex += 2;
1233 } else if (iFirstCount == 1) {
1234 strBuf += kCapUnits[pData[iIndex] - '0'];
1235 ++iIndex;
1236 }
1237 if (iLength > 3 && iFirstCount > 0) {
1238 strBuf += kComm[iComm];
1239 --iComm;
1240 }
1241 while (iIndex < iLength) {
1242 if (pData[iIndex] != '0') {
1243 strBuf += kCapUnits[pData[iIndex] - '0'];
1244 strBuf += kComm[0];
1245 }
1246 if (pData[iIndex + 1] == '0') {
1247 strBuf += kCapUnits[pData[iIndex + 2] - '0'];
1248 } else {
1249 if (pData[iIndex + 1] > '1') {
1250 strBuf += kLastTens[pData[iIndex + 1] - '2'];
1251 strBuf += "-";
1252 strBuf += kUnits[pData[iIndex + 2] - '0'];
1253 } else if (pData[iIndex + 1] == '1') {
1254 strBuf += kTens[pData[iIndex + 2] - '0'];
1255 } else if (pData[iIndex + 1] == '0') {
1256 strBuf += kCapUnits[pData[iIndex + 2] - '0'];
1257 }
1258 }
1259 if (iIndex < iLength - 3) {
1260 strBuf += kComm[iComm];
1261 --iComm;
1262 }
1263 iIndex += 3;
1264 }
1265 return strBuf;
1266 }
1267
WordUS(ByteStringView bsData,int32_t iStyle)1268 ByteString WordUS(ByteStringView bsData, int32_t iStyle) {
1269 if (iStyle < 0 || iStyle > 2)
1270 return ByteString();
1271
1272 int32_t iLength = bsData.GetLength();
1273 ByteString strBuf;
1274 int32_t iIndex = 0;
1275 while (iIndex < iLength) {
1276 if (bsData[iIndex] == '.')
1277 break;
1278 ++iIndex;
1279 }
1280 int32_t iInteger = iIndex;
1281 iIndex = 0;
1282 while (iIndex < iInteger) {
1283 int32_t iCount = (iInteger - iIndex) % 12;
1284 if (!iCount && iInteger - iIndex > 0)
1285 iCount = 12;
1286
1287 strBuf += TrillionUS(bsData.Substr(iIndex, iCount));
1288 iIndex += iCount;
1289 if (iIndex < iInteger)
1290 strBuf += " Trillion ";
1291 }
1292
1293 if (iStyle > 0)
1294 strBuf += " Dollars";
1295
1296 if (iStyle > 1 && iInteger < iLength) {
1297 strBuf += " And ";
1298 iIndex = iInteger + 1;
1299 while (iIndex < iLength) {
1300 int32_t iCount = (iLength - iIndex) % 12;
1301 if (!iCount && iLength - iIndex > 0)
1302 iCount = 12;
1303
1304 strBuf += TrillionUS(bsData.Substr(iIndex, iCount));
1305 iIndex += iCount;
1306 if (iIndex < iLength)
1307 strBuf += " Trillion ";
1308 }
1309 strBuf += " Cents";
1310 }
1311 return strBuf;
1312 }
1313
GetObjectDefaultValue(v8::Isolate * pIsolate,v8::Local<v8::Object> pObject)1314 v8::Local<v8::Value> GetObjectDefaultValue(v8::Isolate* pIsolate,
1315 v8::Local<v8::Object> pObject) {
1316 CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject));
1317 if (!pNode)
1318 return fxv8::NewNullHelper(pIsolate);
1319
1320 v8::Local<v8::Value> value;
1321 pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &value, false,
1322 XFA_Attribute::Unknown);
1323 return value;
1324 }
1325
SetObjectDefaultValue(v8::Isolate * pIsolate,v8::Local<v8::Object> pObject,v8::Local<v8::Value> hNewValue)1326 bool SetObjectDefaultValue(v8::Isolate* pIsolate,
1327 v8::Local<v8::Object> pObject,
1328 v8::Local<v8::Value> hNewValue) {
1329 CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject));
1330 if (!pNode)
1331 return false;
1332
1333 pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &hNewValue, true,
1334 XFA_Attribute::Unknown);
1335 return true;
1336 }
1337
GetExtractedValue(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)1338 v8::Local<v8::Value> GetExtractedValue(v8::Isolate* pIsolate,
1339 v8::Local<v8::Value> pValue) {
1340 if (pValue.IsEmpty())
1341 return v8::Local<v8::Value>();
1342
1343 if (fxv8::IsArray(pValue)) {
1344 v8::Local<v8::Array> arr = pValue.As<v8::Array>();
1345 uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
1346 if (iLength < 3)
1347 return fxv8::NewUndefinedHelper(pIsolate);
1348
1349 v8::Local<v8::Value> propertyValue =
1350 fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
1351 v8::Local<v8::Value> jsValue =
1352 fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2);
1353 if (!fxv8::IsObject(jsValue))
1354 return fxv8::NewUndefinedHelper(pIsolate);
1355
1356 v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
1357 if (fxv8::IsNull(propertyValue))
1358 return GetObjectDefaultValue(pIsolate, jsObjectValue);
1359
1360 ByteString bsName =
1361 fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
1362 return fxv8::ReentrantGetObjectPropertyHelper(pIsolate, jsObjectValue,
1363 bsName.AsStringView());
1364 }
1365
1366 if (fxv8::IsObject(pValue))
1367 return GetObjectDefaultValue(pIsolate, pValue.As<v8::Object>());
1368
1369 return pValue;
1370 }
1371
GetSimpleValue(const v8::FunctionCallbackInfo<v8::Value> & info,uint32_t index)1372 v8::Local<v8::Value> GetSimpleValue(
1373 const v8::FunctionCallbackInfo<v8::Value>& info,
1374 uint32_t index) {
1375 DCHECK(index < static_cast<uint32_t>(info.Length()));
1376 return GetExtractedValue(info.GetIsolate(), info[index]);
1377 }
1378
ValueIsNull(v8::Isolate * pIsolate,v8::Local<v8::Value> arg)1379 bool ValueIsNull(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
1380 v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
1381 return extracted.IsEmpty() || fxv8::IsNull(extracted);
1382 }
1383
ValueToInteger(v8::Isolate * pIsolate,v8::Local<v8::Value> arg)1384 int32_t ValueToInteger(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
1385 v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
1386 if (extracted.IsEmpty())
1387 return 0;
1388
1389 if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
1390 return ValueToInteger(pIsolate, extracted);
1391
1392 if (fxv8::IsString(extracted)) {
1393 ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
1394 return FXSYS_atoi(bsValue.c_str());
1395 }
1396
1397 return fxv8::ReentrantToInt32Helper(pIsolate, extracted);
1398 }
1399
ValueToFloat(v8::Isolate * pIsolate,v8::Local<v8::Value> arg)1400 float ValueToFloat(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
1401 v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
1402 if (extracted.IsEmpty())
1403 return 0.0f;
1404
1405 if (fxv8::IsUndefined(extracted))
1406 return 0.0f;
1407
1408 if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
1409 return ValueToFloat(pIsolate, extracted);
1410
1411 if (fxv8::IsString(extracted)) {
1412 ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
1413 return strtof(bsValue.c_str(), nullptr);
1414 }
1415
1416 return fxv8::ReentrantToFloatHelper(pIsolate, extracted);
1417 }
1418
ValueToDouble(v8::Isolate * pIsolate,v8::Local<v8::Value> arg)1419 double ValueToDouble(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
1420 v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
1421 if (extracted.IsEmpty())
1422 return 0.0;
1423
1424 if (fxv8::IsUndefined(extracted))
1425 return 0.0;
1426
1427 if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
1428 return ValueToDouble(pIsolate, extracted);
1429
1430 if (fxv8::IsString(extracted)) {
1431 ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
1432 return strtod(bsValue.c_str(), nullptr);
1433 }
1434
1435 return fxv8::ReentrantToDoubleHelper(pIsolate, extracted);
1436 }
1437
ExtractDouble(v8::Isolate * pIsolate,v8::Local<v8::Value> src)1438 absl::optional<double> ExtractDouble(v8::Isolate* pIsolate,
1439 v8::Local<v8::Value> src) {
1440 if (src.IsEmpty())
1441 return 0.0;
1442
1443 if (!fxv8::IsArray(src))
1444 return ValueToDouble(pIsolate, src);
1445
1446 v8::Local<v8::Array> arr = src.As<v8::Array>();
1447 uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
1448 if (iLength < 3)
1449 return absl::nullopt;
1450
1451 v8::Local<v8::Value> propertyValue =
1452 fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
1453 v8::Local<v8::Value> jsValue =
1454 fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2);
1455 if (fxv8::IsNull(propertyValue) || !fxv8::IsObject(jsValue))
1456 return ValueToDouble(pIsolate, jsValue);
1457
1458 ByteString bsName =
1459 fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
1460 return ValueToDouble(
1461 pIsolate, fxv8::ReentrantGetObjectPropertyHelper(
1462 pIsolate, jsValue.As<v8::Object>(), bsName.AsStringView()));
1463 }
1464
ValueToUTF8String(v8::Isolate * pIsolate,v8::Local<v8::Value> arg)1465 ByteString ValueToUTF8String(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
1466 if (arg.IsEmpty())
1467 return ByteString();
1468
1469 if (fxv8::IsNull(arg) || fxv8::IsUndefined(arg))
1470 return ByteString();
1471
1472 if (fxv8::IsBoolean(arg))
1473 return fxv8::ReentrantToBooleanHelper(pIsolate, arg) ? "1" : "0";
1474
1475 return fxv8::ReentrantToByteStringHelper(pIsolate, arg);
1476 }
1477
SimpleValueCompare(v8::Isolate * pIsolate,v8::Local<v8::Value> firstValue,v8::Local<v8::Value> secondValue)1478 bool SimpleValueCompare(v8::Isolate* pIsolate,
1479 v8::Local<v8::Value> firstValue,
1480 v8::Local<v8::Value> secondValue) {
1481 if (firstValue.IsEmpty())
1482 return false;
1483
1484 if (fxv8::IsString(firstValue)) {
1485 const ByteString first = ValueToUTF8String(pIsolate, firstValue);
1486 const ByteString second = ValueToUTF8String(pIsolate, secondValue);
1487 return first == second;
1488 }
1489 if (fxv8::IsNumber(firstValue)) {
1490 const float first = ValueToFloat(pIsolate, firstValue);
1491 const float second = ValueToFloat(pIsolate, secondValue);
1492 return first == second;
1493 }
1494 if (fxv8::IsBoolean(firstValue)) {
1495 const bool first = fxv8::ReentrantToBooleanHelper(pIsolate, firstValue);
1496 const bool second = fxv8::ReentrantToBooleanHelper(pIsolate, secondValue);
1497 return first == second;
1498 }
1499 return fxv8::IsNull(firstValue) && fxv8::IsNull(secondValue);
1500 }
1501
UnfoldArgs(const v8::FunctionCallbackInfo<v8::Value> & info)1502 std::vector<v8::Local<v8::Value>> UnfoldArgs(
1503 const v8::FunctionCallbackInfo<v8::Value>& info) {
1504 std::vector<v8::Local<v8::Value>> results;
1505 v8::Isolate* pIsolate = info.GetIsolate();
1506 for (int i = 1; i < info.Length(); ++i) {
1507 v8::Local<v8::Value> arg = info[i];
1508 if (fxv8::IsArray(arg)) {
1509 v8::Local<v8::Array> arr = arg.As<v8::Array>();
1510 uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
1511 if (iLength < 3)
1512 continue;
1513
1514 v8::Local<v8::Value> propertyValue =
1515 fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
1516
1517 for (uint32_t j = 2; j < iLength; j++) {
1518 v8::Local<v8::Value> jsValue =
1519 fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, j);
1520
1521 if (!fxv8::IsObject(jsValue)) {
1522 results.push_back(fxv8::NewUndefinedHelper(pIsolate));
1523 } else if (fxv8::IsNull(propertyValue)) {
1524 results.push_back(
1525 GetObjectDefaultValue(pIsolate, jsValue.As<v8::Object>()));
1526 } else {
1527 ByteString bsName =
1528 fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
1529 results.push_back(fxv8::ReentrantGetObjectPropertyHelper(
1530 pIsolate, jsValue.As<v8::Object>(), bsName.AsStringView()));
1531 }
1532 }
1533 } else if (fxv8::IsObject(arg)) {
1534 results.push_back(GetObjectDefaultValue(pIsolate, arg.As<v8::Object>()));
1535 } else {
1536 results.push_back(arg);
1537 }
1538 }
1539 return results;
1540 }
1541
1542 // Returns empty value on failure.
GetObjectForName(CFXJSE_HostObject * pHostObject,ByteStringView bsAccessorName)1543 v8::Local<v8::Value> GetObjectForName(CFXJSE_HostObject* pHostObject,
1544 ByteStringView bsAccessorName) {
1545 CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument();
1546 if (!pDoc)
1547 return v8::Local<v8::Value>();
1548
1549 CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
1550 absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
1551 pScriptContext->ResolveObjects(
1552 pScriptContext->GetThisObject(),
1553 WideString::FromUTF8(bsAccessorName).AsStringView(),
1554 Mask<XFA_ResolveFlag>{
1555 XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
1556 XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent});
1557 if (!maybeResult.has_value() ||
1558 maybeResult.value().type != CFXJSE_Engine::ResolveResult::Type::kNodes ||
1559 maybeResult.value().objects.empty()) {
1560 return v8::Local<v8::Value>();
1561 }
1562 return pScriptContext->GetOrCreateJSBindingFromMap(
1563 maybeResult.value().objects.front().Get());
1564 }
1565
ResolveObjects(CFXJSE_HostObject * pHostObject,v8::Local<v8::Value> pRefValue,ByteStringView bsSomExp,bool bDotAccessor,bool bHasNoResolveName)1566 absl::optional<CFXJSE_Engine::ResolveResult> ResolveObjects(
1567 CFXJSE_HostObject* pHostObject,
1568 v8::Local<v8::Value> pRefValue,
1569 ByteStringView bsSomExp,
1570 bool bDotAccessor,
1571 bool bHasNoResolveName) {
1572 CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument();
1573 if (!pDoc)
1574 return absl::nullopt;
1575
1576 v8::Isolate* pIsolate = ToFormCalcContext(pHostObject)->GetIsolate();
1577 WideString wsSomExpression = WideString::FromUTF8(bsSomExp);
1578 CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
1579 CXFA_Object* pNode = nullptr;
1580 Mask<XFA_ResolveFlag> dwFlags;
1581 if (bDotAccessor) {
1582 if (fxv8::IsNull(pRefValue)) {
1583 pNode = pScriptContext->GetThisObject();
1584 dwFlags = {XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent};
1585 } else {
1586 pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue);
1587 if (!pNode)
1588 return absl::nullopt;
1589
1590 if (bHasNoResolveName) {
1591 WideString wsName;
1592 if (CXFA_Node* pXFANode = pNode->AsNode()) {
1593 absl::optional<WideString> ret =
1594 pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false);
1595 if (ret.has_value())
1596 wsName = ret.value();
1597 }
1598 if (wsName.IsEmpty())
1599 wsName = L"#" + WideString::FromASCII(pNode->GetClassName());
1600
1601 wsSomExpression = wsName + wsSomExpression;
1602 dwFlags = XFA_ResolveFlag::kSiblings;
1603 } else {
1604 dwFlags = (bsSomExp == "*")
1605 ? Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren}
1606 : Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
1607 XFA_ResolveFlag::kAttributes,
1608 XFA_ResolveFlag::kProperties};
1609 }
1610 }
1611 } else {
1612 pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue);
1613 dwFlags = XFA_ResolveFlag::kAnyChild;
1614 }
1615 return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(),
1616 dwFlags);
1617 }
1618
ParseResolveResult(CFXJSE_HostObject * pHostObject,const CFXJSE_Engine::ResolveResult & resolveNodeRS,v8::Local<v8::Value> pParentValue,bool * bAttribute)1619 std::vector<v8::Local<v8::Value>> ParseResolveResult(
1620 CFXJSE_HostObject* pHostObject,
1621 const CFXJSE_Engine::ResolveResult& resolveNodeRS,
1622 v8::Local<v8::Value> pParentValue,
1623 bool* bAttribute) {
1624 std::vector<v8::Local<v8::Value>> resultValues;
1625 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pHostObject);
1626 v8::Isolate* pIsolate = pContext->GetIsolate();
1627
1628 if (resolveNodeRS.type == CFXJSE_Engine::ResolveResult::Type::kNodes) {
1629 *bAttribute = false;
1630 CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext();
1631 for (auto& pObject : resolveNodeRS.objects) {
1632 resultValues.push_back(
1633 pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get()));
1634 }
1635 return resultValues;
1636 }
1637
1638 *bAttribute = true;
1639 if (resolveNodeRS.script_attribute.callback &&
1640 resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
1641 for (auto& pObject : resolveNodeRS.objects) {
1642 v8::Local<v8::Value> pValue;
1643 CJX_Object* jsObject = pObject->JSObject();
1644 (*resolveNodeRS.script_attribute.callback)(
1645 pIsolate, jsObject, &pValue, false,
1646 resolveNodeRS.script_attribute.attribute);
1647 resultValues.push_back(pValue);
1648 *bAttribute = false;
1649 }
1650 }
1651 if (*bAttribute && fxv8::IsObject(pParentValue))
1652 resultValues.push_back(pParentValue);
1653
1654 return resultValues;
1655 }
1656
1657 // Returns 0 if the provided `arg` is an invalid payment period count.
GetValidatedPaymentPeriods(v8::Isolate * isolate,v8::Local<v8::Value> arg)1658 int GetValidatedPaymentPeriods(v8::Isolate* isolate, v8::Local<v8::Value> arg) {
1659 double periods = ValueToDouble(isolate, arg);
1660 if (periods < 1 ||
1661 periods > static_cast<double>(std::numeric_limits<int32_t>::max())) {
1662 return 0;
1663 }
1664
1665 return static_cast<int>(periods);
1666 }
1667
1668 } // namespace
1669
1670 const FXJSE_CLASS_DESCRIPTOR kFormCalcDescriptor = {
1671 kClassTag, // tag
1672 "XFA_FormCalcClass", // name
1673 kFormCalcFunctions, // methods
1674 std::size(kFormCalcFunctions), // number of methods
1675 nullptr, // dynamic prop type
1676 nullptr, // dynamic prop getter
1677 nullptr, // dynamic prop setter
1678 nullptr, // dynamic prop method call
1679 };
1680
1681 // static
Abs(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1682 void CFXJSE_FormCalcContext::Abs(
1683 CFXJSE_HostObject* pThis,
1684 const v8::FunctionCallbackInfo<v8::Value>& info) {
1685 if (info.Length() != 1) {
1686 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Abs");
1687 return;
1688 }
1689
1690 if (ValueIsNull(info.GetIsolate(), info[0])) {
1691 info.GetReturnValue().SetNull();
1692 return;
1693 }
1694 double dValue = ValueToDouble(info.GetIsolate(), info[0]);
1695 if (dValue < 0)
1696 dValue = -dValue;
1697
1698 info.GetReturnValue().Set(dValue);
1699 }
1700
1701 // static
Avg(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1702 void CFXJSE_FormCalcContext::Avg(
1703 CFXJSE_HostObject* pThis,
1704 const v8::FunctionCallbackInfo<v8::Value>& info) {
1705 uint32_t uCount = 0;
1706 double dSum = 0.0;
1707 auto fn = [&uCount, &dSum](v8::Isolate* pIsolate,
1708 v8::Local<v8::Value> pValue) {
1709 dSum += ValueToDouble(pIsolate, pValue);
1710 uCount++;
1711 };
1712 if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/false))
1713 return;
1714
1715 if (uCount == 0) {
1716 info.GetReturnValue().SetNull();
1717 return;
1718 }
1719 info.GetReturnValue().Set(dSum / uCount);
1720 }
1721
1722 // static
Ceil(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1723 void CFXJSE_FormCalcContext::Ceil(
1724 CFXJSE_HostObject* pThis,
1725 const v8::FunctionCallbackInfo<v8::Value>& info) {
1726 if (info.Length() != 1) {
1727 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ceil");
1728 return;
1729 }
1730
1731 v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
1732 if (ValueIsNull(info.GetIsolate(), argValue)) {
1733 info.GetReturnValue().SetNull();
1734 return;
1735 }
1736
1737 info.GetReturnValue().Set(ceil(ValueToFloat(info.GetIsolate(), argValue)));
1738 }
1739
1740 // static
Count(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1741 void CFXJSE_FormCalcContext::Count(
1742 CFXJSE_HostObject* pThis,
1743 const v8::FunctionCallbackInfo<v8::Value>& info) {
1744 uint32_t iCount = 0;
1745 auto fn = [&iCount](v8::Isolate* pIsolate, v8::Local<v8::Value> pvalue) {
1746 ++iCount;
1747 };
1748 if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
1749 return;
1750
1751 info.GetReturnValue().Set(iCount);
1752 }
1753
1754 // static
Floor(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1755 void CFXJSE_FormCalcContext::Floor(
1756 CFXJSE_HostObject* pThis,
1757 const v8::FunctionCallbackInfo<v8::Value>& info) {
1758 if (info.Length() != 1) {
1759 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Floor");
1760 return;
1761 }
1762
1763 v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
1764 if (ValueIsNull(info.GetIsolate(), argValue)) {
1765 info.GetReturnValue().SetNull();
1766 return;
1767 }
1768
1769 info.GetReturnValue().Set(floor(ValueToFloat(info.GetIsolate(), argValue)));
1770 }
1771
1772 // static
Max(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1773 void CFXJSE_FormCalcContext::Max(
1774 CFXJSE_HostObject* pThis,
1775 const v8::FunctionCallbackInfo<v8::Value>& info) {
1776 uint32_t uCount = 0;
1777 double dMaxValue = 0.0;
1778 auto fn = [&uCount, &dMaxValue](v8::Isolate* pIsolate,
1779 v8::Local<v8::Value> pValue) {
1780 ++uCount;
1781 double dValue = ValueToDouble(pIsolate, pValue);
1782 dMaxValue = uCount == 1 ? dValue : std::max(dMaxValue, dValue);
1783 };
1784 if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
1785 return;
1786
1787 if (uCount == 0) {
1788 info.GetReturnValue().SetNull();
1789 return;
1790 }
1791 info.GetReturnValue().Set(dMaxValue);
1792 }
1793
1794 // static
Min(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1795 void CFXJSE_FormCalcContext::Min(
1796 CFXJSE_HostObject* pThis,
1797 const v8::FunctionCallbackInfo<v8::Value>& info) {
1798 uint32_t uCount = 0;
1799 double dMinValue = 0.0;
1800 auto fn = [&uCount, &dMinValue](v8::Isolate* pIsolate,
1801 v8::Local<v8::Value> pValue) {
1802 ++uCount;
1803 double dValue = ValueToDouble(pIsolate, pValue);
1804 dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
1805 };
1806 if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
1807 return;
1808
1809 if (uCount == 0) {
1810 info.GetReturnValue().SetNull();
1811 return;
1812 }
1813 info.GetReturnValue().Set(dMinValue);
1814 }
1815
1816 // static
Mod(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1817 void CFXJSE_FormCalcContext::Mod(
1818 CFXJSE_HostObject* pThis,
1819 const v8::FunctionCallbackInfo<v8::Value>& info) {
1820 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
1821 if (info.Length() != 2) {
1822 pContext->ThrowParamCountMismatchException("Mod");
1823 return;
1824 }
1825
1826 if (fxv8::IsNull(info[0]) || fxv8::IsNull(info[1])) {
1827 info.GetReturnValue().SetNull();
1828 return;
1829 }
1830
1831 absl::optional<double> maybe_dividend =
1832 ExtractDouble(info.GetIsolate(), info[0]);
1833 absl::optional<double> maybe_divisor =
1834 ExtractDouble(info.GetIsolate(), info[1]);
1835 if (!maybe_dividend.has_value() || !maybe_divisor.has_value()) {
1836 pContext->ThrowArgumentMismatchException();
1837 return;
1838 }
1839
1840 double dividend = maybe_dividend.value();
1841 double divisor = maybe_divisor.value();
1842 if (divisor == 0.0) {
1843 pContext->ThrowDivideByZeroException();
1844 return;
1845 }
1846
1847 info.GetReturnValue().Set(dividend -
1848 divisor * static_cast<int32_t>(dividend / divisor));
1849 }
1850
1851 // static
Round(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1852 void CFXJSE_FormCalcContext::Round(
1853 CFXJSE_HostObject* pThis,
1854 const v8::FunctionCallbackInfo<v8::Value>& info) {
1855 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
1856 int32_t argc = info.Length();
1857 if (argc < 1 || argc > 2) {
1858 pContext->ThrowParamCountMismatchException("Round");
1859 return;
1860 }
1861
1862 if (fxv8::IsNull(info[0])) {
1863 info.GetReturnValue().SetNull();
1864 return;
1865 }
1866
1867 absl::optional<double> maybe_value =
1868 ExtractDouble(info.GetIsolate(), info[0]);
1869 if (!maybe_value.has_value()) {
1870 pContext->ThrowArgumentMismatchException();
1871 return;
1872 }
1873
1874 double dValue = maybe_value.value();
1875 uint8_t uPrecision = 0;
1876 if (argc > 1) {
1877 if (fxv8::IsNull(info[1])) {
1878 info.GetReturnValue().SetNull();
1879 return;
1880 }
1881 absl::optional<double> maybe_precision =
1882 ExtractDouble(info.GetIsolate(), info[1]);
1883 if (!maybe_precision.has_value()) {
1884 pContext->ThrowArgumentMismatchException();
1885 return;
1886 }
1887 double dPrecision = maybe_precision.value();
1888 uPrecision = static_cast<uint8_t>(pdfium::clamp(dPrecision, 0.0, 12.0));
1889 }
1890
1891 CFGAS_Decimal decimalValue(static_cast<float>(dValue), uPrecision);
1892 info.GetReturnValue().Set(decimalValue.ToDouble());
1893 }
1894
1895 // static
Sum(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1896 void CFXJSE_FormCalcContext::Sum(
1897 CFXJSE_HostObject* pThis,
1898 const v8::FunctionCallbackInfo<v8::Value>& info) {
1899 uint32_t uCount = 0;
1900 double dSum = 0.0;
1901 auto fn = [&uCount, &dSum](v8::Isolate* pIsolate,
1902 v8::Local<v8::Value> pValue) {
1903 ++uCount;
1904 dSum += ValueToDouble(pIsolate, pValue);
1905 };
1906 if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
1907 return;
1908
1909 if (uCount == 0) {
1910 info.GetReturnValue().SetNull();
1911 return;
1912 }
1913 info.GetReturnValue().Set(dSum);
1914 }
1915
1916 // static
Date(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1917 void CFXJSE_FormCalcContext::Date(
1918 CFXJSE_HostObject* pThis,
1919 const v8::FunctionCallbackInfo<v8::Value>& info) {
1920 if (info.Length() != 0) {
1921 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date");
1922 return;
1923 }
1924
1925 time_t currentTime;
1926 FXSYS_time(¤tTime);
1927 struct tm* pTmStruct = gmtime(¤tTime);
1928
1929 info.GetReturnValue().Set(DateString2Num(
1930 ByteString::Format("%d%02d%02d", pTmStruct->tm_year + 1900,
1931 pTmStruct->tm_mon + 1, pTmStruct->tm_mday)
1932 .AsStringView()));
1933 }
1934
1935 // static
Date2Num(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1936 void CFXJSE_FormCalcContext::Date2Num(
1937 CFXJSE_HostObject* pThis,
1938 const v8::FunctionCallbackInfo<v8::Value>& info) {
1939 int32_t argc = info.Length();
1940 if (argc < 1 || argc > 3) {
1941 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num");
1942 return;
1943 }
1944
1945 v8::Local<v8::Value> dateValue = GetSimpleValue(info, 0);
1946 if (ValueIsNull(info.GetIsolate(), dateValue)) {
1947 info.GetReturnValue().SetNull();
1948 return;
1949 }
1950
1951 ByteString bsDate = ValueToUTF8String(info.GetIsolate(), dateValue);
1952 ByteString bsFormat;
1953 if (argc > 1) {
1954 v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
1955 if (ValueIsNull(info.GetIsolate(), formatValue)) {
1956 info.GetReturnValue().SetNull();
1957 return;
1958 }
1959 bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
1960 }
1961
1962 ByteString bsLocale;
1963 if (argc > 2) {
1964 v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
1965 if (ValueIsNull(info.GetIsolate(), localeValue)) {
1966 info.GetReturnValue().SetNull();
1967 return;
1968 }
1969 bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
1970 }
1971
1972 ByteString bsIsoDate =
1973 Local2IsoDate(pThis, bsDate.AsStringView(), bsFormat.AsStringView(),
1974 bsLocale.AsStringView());
1975 info.GetReturnValue().Set(DateString2Num(bsIsoDate.AsStringView()));
1976 }
1977
1978 // static
DateFmt(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)1979 void CFXJSE_FormCalcContext::DateFmt(
1980 CFXJSE_HostObject* pThis,
1981 const v8::FunctionCallbackInfo<v8::Value>& info) {
1982 int32_t argc = info.Length();
1983 if (argc > 2) {
1984 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num");
1985 return;
1986 }
1987
1988 int32_t iStyle = 0;
1989 if (argc > 0) {
1990 v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
1991 if (fxv8::IsNull(infotyle)) {
1992 info.GetReturnValue().SetNull();
1993 return;
1994 }
1995
1996 iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
1997 if (iStyle < 0 || iStyle > 4)
1998 iStyle = 0;
1999 }
2000
2001 ByteString bsLocale;
2002 if (argc > 1) {
2003 v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
2004 if (fxv8::IsNull(argLocale)) {
2005 info.GetReturnValue().SetNull();
2006 return;
2007 }
2008 bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
2009 }
2010
2011 ByteString bsFormat =
2012 GetStandardDateFormat(pThis, iStyle, bsLocale.AsStringView());
2013 info.GetReturnValue().Set(
2014 fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
2015 }
2016
2017 // static
IsoDate2Num(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2018 void CFXJSE_FormCalcContext::IsoDate2Num(
2019 CFXJSE_HostObject* pThis,
2020 const v8::FunctionCallbackInfo<v8::Value>& info) {
2021 if (info.Length() != 1) {
2022 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("IsoDate2Num");
2023 return;
2024 }
2025 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
2026 if (fxv8::IsNull(argOne)) {
2027 info.GetReturnValue().SetNull();
2028 return;
2029 }
2030 ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
2031 info.GetReturnValue().Set(DateString2Num(bsArg.AsStringView()));
2032 }
2033
2034 // static
IsoTime2Num(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2035 void CFXJSE_FormCalcContext::IsoTime2Num(
2036 CFXJSE_HostObject* pThis,
2037 const v8::FunctionCallbackInfo<v8::Value>& info) {
2038 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
2039 if (info.Length() != 1) {
2040 pContext->ThrowParamCountMismatchException("IsoTime2Num");
2041 return;
2042 }
2043
2044 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
2045 if (ValueIsNull(info.GetIsolate(), argOne)) {
2046 info.GetReturnValue().SetNull();
2047 return;
2048 }
2049
2050 CXFA_Document* pDoc = pContext->GetDocument();
2051 CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
2052 ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
2053 auto pos = bsArg.Find('T', 0);
2054 if (!pos.has_value() || pos.value() == bsArg.GetLength() - 1) {
2055 info.GetReturnValue().Set(0);
2056 return;
2057 }
2058 bsArg = bsArg.Last(bsArg.GetLength() - (pos.value() + 1));
2059
2060 CXFA_LocaleValue timeValue(CXFA_LocaleValue::ValueType::kTime,
2061 WideString::FromUTF8(bsArg.AsStringView()), pMgr);
2062 if (!timeValue.IsValid()) {
2063 info.GetReturnValue().Set(0);
2064 return;
2065 }
2066
2067 CFX_DateTime uniTime = timeValue.GetTime();
2068 int32_t hour = uniTime.GetHour();
2069 int32_t min = uniTime.GetMinute();
2070 int32_t second = uniTime.GetSecond();
2071 int32_t milSecond = uniTime.GetMillisecond();
2072
2073 // TODO(dsinclair): See if there is other time conversion code in pdfium and
2074 // consolidate.
2075 int32_t mins = hour * 60 + min;
2076 mins -= pMgr->GetDefLocale()->GetTimeZoneInMinutes();
2077 while (mins > 1440)
2078 mins -= 1440;
2079 while (mins < 0)
2080 mins += 1440;
2081 hour = mins / 60;
2082 min = mins % 60;
2083
2084 info.GetReturnValue().Set(hour * 3600000 + min * 60000 + second * 1000 +
2085 milSecond + 1);
2086 }
2087
2088 // static
LocalDateFmt(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2089 void CFXJSE_FormCalcContext::LocalDateFmt(
2090 CFXJSE_HostObject* pThis,
2091 const v8::FunctionCallbackInfo<v8::Value>& info) {
2092 int32_t argc = info.Length();
2093 if (argc > 2) {
2094 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalDateFmt");
2095 return;
2096 }
2097
2098 int32_t iStyle = 0;
2099 if (argc > 0) {
2100 v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
2101 if (fxv8::IsNull(infotyle)) {
2102 info.GetReturnValue().SetNull();
2103 return;
2104 }
2105 iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
2106 if (iStyle > 4 || iStyle < 0)
2107 iStyle = 0;
2108 }
2109
2110 ByteString bsLocale;
2111 if (argc > 1) {
2112 v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
2113 if (fxv8::IsNull(argLocale)) {
2114 info.GetReturnValue().SetNull();
2115 return;
2116 }
2117 bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
2118 }
2119
2120 ByteString bsFormat =
2121 GetLocalDateFormat(pThis, iStyle, bsLocale.AsStringView(), false);
2122 info.GetReturnValue().Set(
2123 fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
2124 }
2125
2126 // static
LocalTimeFmt(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2127 void CFXJSE_FormCalcContext::LocalTimeFmt(
2128 CFXJSE_HostObject* pThis,
2129 const v8::FunctionCallbackInfo<v8::Value>& info) {
2130 int32_t argc = info.Length();
2131 if (argc > 2) {
2132 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalTimeFmt");
2133 return;
2134 }
2135
2136 int32_t iStyle = 0;
2137 if (argc > 0) {
2138 v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
2139 if (fxv8::IsNull(infotyle)) {
2140 info.GetReturnValue().SetNull();
2141 return;
2142 }
2143 iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
2144 if (iStyle > 4 || iStyle < 0)
2145 iStyle = 0;
2146 }
2147
2148 ByteString bsLocale;
2149 if (argc > 1) {
2150 v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
2151 if (fxv8::IsNull(argLocale)) {
2152 info.GetReturnValue().SetNull();
2153 return;
2154 }
2155 bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
2156 }
2157
2158 ByteString bsFormat =
2159 GetLocalTimeFormat(pThis, iStyle, bsLocale.AsStringView(), false);
2160 info.GetReturnValue().Set(
2161 fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
2162 }
2163
2164 // static
Num2Date(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2165 void CFXJSE_FormCalcContext::Num2Date(
2166 CFXJSE_HostObject* pThis,
2167 const v8::FunctionCallbackInfo<v8::Value>& info) {
2168 int32_t argc = info.Length();
2169 if (argc < 1 || argc > 3) {
2170 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Date");
2171 return;
2172 }
2173
2174 v8::Local<v8::Value> dateValue = GetSimpleValue(info, 0);
2175 if (ValueIsNull(info.GetIsolate(), dateValue)) {
2176 info.GetReturnValue().SetNull();
2177 return;
2178 }
2179 int32_t dDate =
2180 static_cast<int32_t>(ValueToFloat(info.GetIsolate(), dateValue));
2181 if (dDate < 1) {
2182 info.GetReturnValue().SetNull();
2183 return;
2184 }
2185
2186 ByteString bsFormat;
2187 if (argc > 1) {
2188 v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
2189 if (ValueIsNull(info.GetIsolate(), formatValue)) {
2190 info.GetReturnValue().SetNull();
2191 return;
2192 }
2193 bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
2194 }
2195
2196 ByteString bsLocale;
2197 if (argc > 2) {
2198 v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
2199 if (ValueIsNull(info.GetIsolate(), localeValue)) {
2200 info.GetReturnValue().SetNull();
2201 return;
2202 }
2203 bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
2204 }
2205
2206 int32_t iYear = 1900;
2207 int32_t iMonth = 1;
2208 int32_t iDay = 1;
2209 int32_t i = 0;
2210 while (dDate > 0) {
2211 if (iMonth == 2) {
2212 if ((!((iYear + i) % 4) && ((iYear + i) % 100)) || !((iYear + i) % 400)) {
2213 if (dDate > 29) {
2214 ++iMonth;
2215 if (iMonth > 12) {
2216 iMonth = 1;
2217 ++i;
2218 }
2219 iDay = 1;
2220 dDate -= 29;
2221 } else {
2222 iDay += static_cast<int32_t>(dDate) - 1;
2223 dDate = 0;
2224 }
2225 } else {
2226 if (dDate > 28) {
2227 ++iMonth;
2228 if (iMonth > 12) {
2229 iMonth = 1;
2230 ++i;
2231 }
2232 iDay = 1;
2233 dDate -= 28;
2234 } else {
2235 iDay += static_cast<int32_t>(dDate) - 1;
2236 dDate = 0;
2237 }
2238 }
2239 } else if (iMonth < 8) {
2240 if ((iMonth % 2 == 0)) {
2241 if (dDate > 30) {
2242 ++iMonth;
2243 if (iMonth > 12) {
2244 iMonth = 1;
2245 ++i;
2246 }
2247 iDay = 1;
2248 dDate -= 30;
2249 } else {
2250 iDay += static_cast<int32_t>(dDate) - 1;
2251 dDate = 0;
2252 }
2253 } else {
2254 if (dDate > 31) {
2255 ++iMonth;
2256 if (iMonth > 12) {
2257 iMonth = 1;
2258 ++i;
2259 }
2260 iDay = 1;
2261 dDate -= 31;
2262 } else {
2263 iDay += static_cast<int32_t>(dDate) - 1;
2264 dDate = 0;
2265 }
2266 }
2267 } else {
2268 if (iMonth % 2 != 0) {
2269 if (dDate > 30) {
2270 ++iMonth;
2271 if (iMonth > 12) {
2272 iMonth = 1;
2273 ++i;
2274 }
2275 iDay = 1;
2276 dDate -= 30;
2277 } else {
2278 iDay += static_cast<int32_t>(dDate) - 1;
2279 dDate = 0;
2280 }
2281 } else {
2282 if (dDate > 31) {
2283 ++iMonth;
2284 if (iMonth > 12) {
2285 iMonth = 1;
2286 ++i;
2287 }
2288 iDay = 1;
2289 dDate -= 31;
2290 } else {
2291 iDay += static_cast<int32_t>(dDate) - 1;
2292 dDate = 0;
2293 }
2294 }
2295 }
2296 }
2297
2298 ByteString bsLocalDate = IsoDate2Local(
2299 pThis,
2300 ByteString::Format("%d%02d%02d", iYear + i, iMonth, iDay).AsStringView(),
2301 bsFormat.AsStringView(), bsLocale.AsStringView());
2302 info.GetReturnValue().Set(
2303 fxv8::NewStringHelper(info.GetIsolate(), bsLocalDate.AsStringView()));
2304 }
2305
2306 // static
Num2GMTime(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2307 void CFXJSE_FormCalcContext::Num2GMTime(
2308 CFXJSE_HostObject* pThis,
2309 const v8::FunctionCallbackInfo<v8::Value>& info) {
2310 int32_t argc = info.Length();
2311 if (argc < 1 || argc > 3) {
2312 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2GMTime");
2313 return;
2314 }
2315
2316 v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
2317 if (fxv8::IsNull(timeValue)) {
2318 info.GetReturnValue().SetNull();
2319 return;
2320 }
2321 int32_t iTime =
2322 static_cast<int32_t>(ValueToFloat(info.GetIsolate(), timeValue));
2323 if (abs(iTime) < 1.0) {
2324 info.GetReturnValue().SetNull();
2325 return;
2326 }
2327
2328 ByteString bsFormat;
2329 if (argc > 1) {
2330 v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
2331 if (fxv8::IsNull(formatValue)) {
2332 info.GetReturnValue().SetNull();
2333 return;
2334 }
2335 bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
2336 }
2337
2338 ByteString bsLocale;
2339 if (argc > 2) {
2340 v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
2341 if (fxv8::IsNull(localeValue)) {
2342 info.GetReturnValue().SetNull();
2343 return;
2344 }
2345 bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
2346 }
2347
2348 ByteString bsGMTTime = Num2AllTime(pThis, iTime, bsFormat.AsStringView(),
2349 bsLocale.AsStringView(), true);
2350 info.GetReturnValue().Set(
2351 fxv8::NewStringHelper(info.GetIsolate(), bsGMTTime.AsStringView()));
2352 }
2353
2354 // static
Num2Time(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2355 void CFXJSE_FormCalcContext::Num2Time(
2356 CFXJSE_HostObject* pThis,
2357 const v8::FunctionCallbackInfo<v8::Value>& info) {
2358 int32_t argc = info.Length();
2359 if (argc < 1 || argc > 3) {
2360 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Time");
2361 return;
2362 }
2363
2364 v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
2365 if (fxv8::IsNull(timeValue)) {
2366 info.GetReturnValue().SetNull();
2367 return;
2368 }
2369 float fTime = ValueToFloat(info.GetIsolate(), timeValue);
2370 if (fabs(fTime) < 1.0) {
2371 info.GetReturnValue().SetNull();
2372 return;
2373 }
2374
2375 ByteString bsFormat;
2376 if (argc > 1) {
2377 v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
2378 if (fxv8::IsNull(formatValue)) {
2379 info.GetReturnValue().SetNull();
2380 return;
2381 }
2382 bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
2383 }
2384
2385 ByteString bsLocale;
2386 if (argc > 2) {
2387 v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
2388 if (fxv8::IsNull(localeValue)) {
2389 info.GetReturnValue().SetNull();
2390 return;
2391 }
2392 bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
2393 }
2394
2395 ByteString bsLocalTime =
2396 Num2AllTime(pThis, static_cast<int32_t>(fTime), bsFormat.AsStringView(),
2397 bsLocale.AsStringView(), false);
2398 info.GetReturnValue().Set(
2399 fxv8::NewStringHelper(info.GetIsolate(), bsLocalTime.AsStringView()));
2400 }
2401
2402 // static
Time(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2403 void CFXJSE_FormCalcContext::Time(
2404 CFXJSE_HostObject* pThis,
2405 const v8::FunctionCallbackInfo<v8::Value>& info) {
2406 if (info.Length() != 0) {
2407 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time");
2408 return;
2409 }
2410
2411 time_t now;
2412 FXSYS_time(&now);
2413 struct tm* pGmt = gmtime(&now);
2414 info.GetReturnValue().Set(
2415 (pGmt->tm_hour * 3600 + pGmt->tm_min * 60 + pGmt->tm_sec) * 1000);
2416 }
2417
2418 // static
Time2Num(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2419 void CFXJSE_FormCalcContext::Time2Num(
2420 CFXJSE_HostObject* pThis,
2421 const v8::FunctionCallbackInfo<v8::Value>& info) {
2422 int32_t argc = info.Length();
2423 if (argc < 1 || argc > 3) {
2424 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time2Num");
2425 return;
2426 }
2427
2428 ByteString bsTime;
2429 v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
2430 if (ValueIsNull(info.GetIsolate(), timeValue)) {
2431 info.GetReturnValue().SetNull();
2432 return;
2433 }
2434 bsTime = ValueToUTF8String(info.GetIsolate(), timeValue);
2435
2436 ByteString bsFormat;
2437 if (argc > 1) {
2438 v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
2439 if (ValueIsNull(info.GetIsolate(), formatValue)) {
2440 info.GetReturnValue().SetNull();
2441 return;
2442 }
2443 bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
2444 }
2445
2446 ByteString bsLocale;
2447 if (argc > 2) {
2448 v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
2449 if (ValueIsNull(info.GetIsolate(), localeValue)) {
2450 info.GetReturnValue().SetNull();
2451 return;
2452 }
2453 bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
2454 }
2455
2456 CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
2457 CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
2458 GCedLocaleIface* pLocale = nullptr;
2459 if (!bsLocale.IsEmpty()) {
2460 pLocale =
2461 pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale.AsStringView()));
2462 }
2463 if (!pLocale) {
2464 CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
2465 pLocale = pThisNode->GetLocale();
2466 }
2467
2468 WideString wsFormat;
2469 if (bsFormat.IsEmpty()) {
2470 wsFormat =
2471 pLocale->GetTimePattern(LocaleIface::DateTimeSubcategory::kDefault);
2472 } else {
2473 wsFormat = WideString::FromUTF8(bsFormat.AsStringView());
2474 }
2475 wsFormat = L"time{" + wsFormat + L"}";
2476 CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kTime,
2477 WideString::FromUTF8(bsTime.AsStringView()),
2478 wsFormat, pLocale, pMgr);
2479 if (!localeValue.IsValid()) {
2480 info.GetReturnValue().Set(0);
2481 return;
2482 }
2483
2484 CFX_DateTime uniTime = localeValue.GetTime();
2485 int32_t hour = uniTime.GetHour();
2486 int32_t minute = uniTime.GetMinute();
2487 const int32_t second = uniTime.GetSecond();
2488 const int32_t millisecond = uniTime.GetMillisecond();
2489
2490 constexpr int kMinutesInDay = 24 * 60;
2491 int32_t minutes_with_tz =
2492 hour * 60 + minute - CXFA_TimeZoneProvider().GetTimeZoneInMinutes();
2493 minutes_with_tz %= kMinutesInDay;
2494 if (minutes_with_tz < 0)
2495 minutes_with_tz += kMinutesInDay;
2496
2497 hour = minutes_with_tz / 60;
2498 minute = minutes_with_tz % 60;
2499 info.GetReturnValue().Set(hour * 3600000 + minute * 60000 + second * 1000 +
2500 millisecond + 1);
2501 }
2502
2503 // static
TimeFmt(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2504 void CFXJSE_FormCalcContext::TimeFmt(
2505 CFXJSE_HostObject* pThis,
2506 const v8::FunctionCallbackInfo<v8::Value>& info) {
2507 int32_t argc = info.Length();
2508 if (argc > 2) {
2509 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("TimeFmt");
2510 return;
2511 }
2512
2513 int32_t iStyle = 0;
2514 if (argc > 0) {
2515 v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
2516 if (fxv8::IsNull(infotyle)) {
2517 info.GetReturnValue().SetNull();
2518 return;
2519 }
2520 iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
2521 if (iStyle > 4 || iStyle < 0)
2522 iStyle = 0;
2523 }
2524
2525 ByteString bsLocale;
2526 if (argc > 1) {
2527 v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
2528 if (fxv8::IsNull(argLocale)) {
2529 info.GetReturnValue().SetNull();
2530 return;
2531 }
2532 bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
2533 }
2534
2535 ByteString bsFormat =
2536 GetStandardTimeFormat(pThis, iStyle, bsLocale.AsStringView());
2537 info.GetReturnValue().Set(
2538 fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
2539 }
2540
2541 // static
Local2IsoDate(CFXJSE_HostObject * pThis,ByteStringView bsDate,ByteStringView bsFormat,ByteStringView bsLocale)2542 ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_HostObject* pThis,
2543 ByteStringView bsDate,
2544 ByteStringView bsFormat,
2545 ByteStringView bsLocale) {
2546 CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
2547 if (!pDoc)
2548 return ByteString();
2549
2550 CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
2551 GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
2552 if (!pLocale)
2553 return ByteString();
2554
2555 WideString wsFormat = FormatFromString(pLocale, bsFormat);
2556 CFX_DateTime dt =
2557 CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate,
2558 WideString::FromUTF8(bsDate), wsFormat, pLocale, pMgr)
2559 .GetDate();
2560
2561 return ByteString::Format("%4d-%02d-%02d", dt.GetYear(), dt.GetMonth(),
2562 dt.GetDay());
2563 }
2564
2565 // static
IsoDate2Local(CFXJSE_HostObject * pThis,ByteStringView bsDate,ByteStringView bsFormat,ByteStringView bsLocale)2566 ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_HostObject* pThis,
2567 ByteStringView bsDate,
2568 ByteStringView bsFormat,
2569 ByteStringView bsLocale) {
2570 CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
2571 if (!pDoc)
2572 return ByteString();
2573
2574 CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
2575 GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
2576 if (!pLocale)
2577 return ByteString();
2578
2579 WideString wsFormat = FormatFromString(pLocale, bsFormat);
2580 WideString wsRet;
2581 CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate,
2582 WideString::FromUTF8(bsDate), pMgr)
2583 .FormatPatterns(wsRet, wsFormat, pLocale, XFA_ValuePicture::kDisplay);
2584 return wsRet.ToUTF8();
2585 }
2586
2587 // static
IsoTime2Local(CFXJSE_HostObject * pThis,ByteStringView bsTime,ByteStringView bsFormat,ByteStringView bsLocale)2588 ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_HostObject* pThis,
2589 ByteStringView bsTime,
2590 ByteStringView bsFormat,
2591 ByteStringView bsLocale) {
2592 CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
2593 if (!pDoc)
2594 return ByteString();
2595
2596 CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
2597 GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
2598 if (!pLocale)
2599 return ByteString();
2600
2601 WideString wsFormat = {
2602 L"time{", FormatFromString(pLocale, bsFormat).AsStringView(), L"}"};
2603 CXFA_LocaleValue widgetValue(CXFA_LocaleValue::ValueType::kTime,
2604 WideString::FromUTF8(bsTime), pMgr);
2605 WideString wsRet;
2606 widgetValue.FormatPatterns(wsRet, wsFormat, pLocale,
2607 XFA_ValuePicture::kDisplay);
2608 return wsRet.ToUTF8();
2609 }
2610
2611 // static
GetLocalDateFormat(CFXJSE_HostObject * pThis,int32_t iStyle,ByteStringView bsLocale,bool bStandard)2612 ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_HostObject* pThis,
2613 int32_t iStyle,
2614 ByteStringView bsLocale,
2615 bool bStandard) {
2616 CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
2617 if (!pDoc)
2618 return ByteString();
2619
2620 return GetLocalDateTimeFormat(pDoc, iStyle, bsLocale, bStandard,
2621 /*bIsDate=*/true);
2622 }
2623
2624 // static
GetLocalTimeFormat(CFXJSE_HostObject * pThis,int32_t iStyle,ByteStringView bsLocale,bool bStandard)2625 ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_HostObject* pThis,
2626 int32_t iStyle,
2627 ByteStringView bsLocale,
2628 bool bStandard) {
2629 CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
2630 if (!pDoc)
2631 return ByteString();
2632
2633 return GetLocalDateTimeFormat(pDoc, iStyle, bsLocale, bStandard,
2634 /*bIsDate=*/false);
2635 }
2636
2637 // static
GetStandardDateFormat(CFXJSE_HostObject * pThis,int32_t iStyle,ByteStringView bsLocale)2638 ByteString CFXJSE_FormCalcContext::GetStandardDateFormat(
2639 CFXJSE_HostObject* pThis,
2640 int32_t iStyle,
2641 ByteStringView bsLocale) {
2642 return GetLocalDateFormat(pThis, iStyle, bsLocale, true);
2643 }
2644
2645 // static
GetStandardTimeFormat(CFXJSE_HostObject * pThis,int32_t iStyle,ByteStringView bsLocale)2646 ByteString CFXJSE_FormCalcContext::GetStandardTimeFormat(
2647 CFXJSE_HostObject* pThis,
2648 int32_t iStyle,
2649 ByteStringView bsLocale) {
2650 return GetLocalTimeFormat(pThis, iStyle, bsLocale, true);
2651 }
2652
2653 // static
Num2AllTime(CFXJSE_HostObject * pThis,int32_t iTime,ByteStringView bsFormat,ByteStringView bsLocale,bool bGM)2654 ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_HostObject* pThis,
2655 int32_t iTime,
2656 ByteStringView bsFormat,
2657 ByteStringView bsLocale,
2658 bool bGM) {
2659 int32_t iHour = 0;
2660 int32_t iMin = 0;
2661 int32_t iSec = 0;
2662 iHour = static_cast<int>(iTime) / 3600000;
2663 iMin = (static_cast<int>(iTime) - iHour * 3600000) / 60000;
2664 iSec = (static_cast<int>(iTime) - iHour * 3600000 - iMin * 60000) / 1000;
2665
2666 if (!bGM) {
2667 int32_t iZoneHour;
2668 int32_t iZoneMin;
2669 int32_t iZoneSec;
2670 GetLocalTimeZone(&iZoneHour, &iZoneMin, &iZoneSec);
2671 iHour += iZoneHour;
2672 iMin += iZoneMin;
2673 iSec += iZoneSec;
2674 }
2675
2676 return IsoTime2Local(
2677 pThis,
2678 ByteString::Format("%02d:%02d:%02d", iHour, iMin, iSec).AsStringView(),
2679 bsFormat, bsLocale);
2680 }
2681
2682 // static
Apr(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2683 void CFXJSE_FormCalcContext::Apr(
2684 CFXJSE_HostObject* pThis,
2685 const v8::FunctionCallbackInfo<v8::Value>& info) {
2686 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
2687 if (info.Length() != 3) {
2688 pContext->ThrowParamCountMismatchException("Apr");
2689 return;
2690 }
2691
2692 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
2693 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
2694 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
2695 if (ValueIsNull(info.GetIsolate(), argOne) ||
2696 ValueIsNull(info.GetIsolate(), argTwo) ||
2697 ValueIsNull(info.GetIsolate(), argThree)) {
2698 info.GetReturnValue().SetNull();
2699 return;
2700 }
2701
2702 double nPrincipal = ValueToDouble(info.GetIsolate(), argOne);
2703 double nPayment = ValueToDouble(info.GetIsolate(), argTwo);
2704 int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
2705 if (nPrincipal <= 0 || nPayment <= 0 || nPeriods == 0) {
2706 pContext->ThrowArgumentMismatchException();
2707 return;
2708 }
2709
2710 double r = 2 * (nPeriods * nPayment - nPrincipal) / (nPeriods * nPrincipal);
2711 double nTemp = 1;
2712 for (int32_t i = 0; i < nPeriods; ++i)
2713 nTemp *= (1 + r);
2714
2715 double nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
2716 while (fabs(nRet) > kFinancialPrecision) {
2717 double nDerivative =
2718 ((nTemp + r * nPeriods * (nTemp / (1 + r))) * (nTemp - 1) -
2719 (r * nTemp * nPeriods * (nTemp / (1 + r)))) /
2720 ((nTemp - 1) * (nTemp - 1));
2721 if (nDerivative == 0) {
2722 info.GetReturnValue().SetNull();
2723 return;
2724 }
2725
2726 r = r - nRet / nDerivative;
2727 nTemp = 1;
2728 for (int32_t i = 0; i < nPeriods; ++i) {
2729 nTemp *= (1 + r);
2730 }
2731 nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
2732 }
2733 info.GetReturnValue().Set(r * 12);
2734 }
2735
2736 // static
CTerm(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2737 void CFXJSE_FormCalcContext::CTerm(
2738 CFXJSE_HostObject* pThis,
2739 const v8::FunctionCallbackInfo<v8::Value>& info) {
2740 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
2741 if (info.Length() != 3) {
2742 pContext->ThrowParamCountMismatchException("CTerm");
2743 return;
2744 }
2745
2746 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
2747 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
2748 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
2749 if (ValueIsNull(info.GetIsolate(), argOne) ||
2750 ValueIsNull(info.GetIsolate(), argTwo) ||
2751 ValueIsNull(info.GetIsolate(), argThree)) {
2752 info.GetReturnValue().SetNull();
2753 return;
2754 }
2755
2756 float nRate = ValueToFloat(info.GetIsolate(), argOne);
2757 float nFutureValue = ValueToFloat(info.GetIsolate(), argTwo);
2758 float nInitAmount = ValueToFloat(info.GetIsolate(), argThree);
2759 if ((nRate <= 0) || (nFutureValue <= 0) || (nInitAmount <= 0)) {
2760 pContext->ThrowArgumentMismatchException();
2761 return;
2762 }
2763
2764 info.GetReturnValue().Set(log((float)(nFutureValue / nInitAmount)) /
2765 log((float)(1 + nRate)));
2766 }
2767
2768 // static
FV(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2769 void CFXJSE_FormCalcContext::FV(
2770 CFXJSE_HostObject* pThis,
2771 const v8::FunctionCallbackInfo<v8::Value>& info) {
2772 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
2773 if (info.Length() != 3) {
2774 pContext->ThrowParamCountMismatchException("FV");
2775 return;
2776 }
2777
2778 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
2779 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
2780 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
2781 if (ValueIsNull(info.GetIsolate(), argOne) ||
2782 ValueIsNull(info.GetIsolate(), argTwo) ||
2783 ValueIsNull(info.GetIsolate(), argThree)) {
2784 info.GetReturnValue().SetNull();
2785 return;
2786 }
2787
2788 double nAmount = ValueToDouble(info.GetIsolate(), argOne);
2789 double nRate = ValueToDouble(info.GetIsolate(), argTwo);
2790 int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
2791 if (nAmount <= 0 || nRate < 0 || nPeriods == 0) {
2792 pContext->ThrowArgumentMismatchException();
2793 return;
2794 }
2795
2796 double dResult = 0;
2797 if (nRate) {
2798 double nTemp = 1;
2799 for (int i = 0; i < nPeriods; ++i) {
2800 nTemp *= 1 + nRate;
2801 }
2802 dResult = nAmount * (nTemp - 1) / nRate;
2803 } else {
2804 dResult = nAmount * nPeriods;
2805 }
2806
2807 info.GetReturnValue().Set(dResult);
2808 }
2809
2810 // static
IPmt(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2811 void CFXJSE_FormCalcContext::IPmt(
2812 CFXJSE_HostObject* pThis,
2813 const v8::FunctionCallbackInfo<v8::Value>& info) {
2814 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
2815 if (info.Length() != 5) {
2816 pContext->ThrowParamCountMismatchException("IPmt");
2817 return;
2818 }
2819
2820 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
2821 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
2822 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
2823 v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
2824 v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
2825 if (ValueIsNull(info.GetIsolate(), argOne) ||
2826 ValueIsNull(info.GetIsolate(), argTwo) ||
2827 ValueIsNull(info.GetIsolate(), argThree) ||
2828 ValueIsNull(info.GetIsolate(), argFour) ||
2829 ValueIsNull(info.GetIsolate(), argFive)) {
2830 info.GetReturnValue().SetNull();
2831 return;
2832 }
2833
2834 float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne);
2835 float nRate = ValueToFloat(info.GetIsolate(), argTwo);
2836 float nPayment = ValueToFloat(info.GetIsolate(), argThree);
2837 float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour);
2838 float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive);
2839 if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
2840 (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
2841 pContext->ThrowArgumentMismatchException();
2842 return;
2843 }
2844
2845 float nRateOfMonth = nRate / 12;
2846 int32_t iNums = static_cast<int32_t>(
2847 (log10((float)(nPayment / nPrincipalAmount)) -
2848 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
2849 log10((float)(1 + nRateOfMonth)));
2850 int32_t iEnd =
2851 std::min(static_cast<int32_t>(nFirstMonth + nNumberOfMonths - 1), iNums);
2852
2853 if (nPayment < nPrincipalAmount * nRateOfMonth) {
2854 info.GetReturnValue().Set(0);
2855 return;
2856 }
2857
2858 int32_t i = 0;
2859 for (i = 0; i < nFirstMonth - 1; ++i)
2860 nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
2861
2862 float nSum = 0;
2863 for (; i < iEnd; ++i) {
2864 nSum += nPrincipalAmount * nRateOfMonth;
2865 nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
2866 }
2867 info.GetReturnValue().Set(nSum);
2868 }
2869
2870 // static
NPV(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2871 void CFXJSE_FormCalcContext::NPV(
2872 CFXJSE_HostObject* pThis,
2873 const v8::FunctionCallbackInfo<v8::Value>& info) {
2874 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
2875 int32_t argc = info.Length();
2876 if (argc < 2) {
2877 pContext->ThrowParamCountMismatchException("NPV");
2878 return;
2879 }
2880
2881 v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
2882 if (ValueIsNull(info.GetIsolate(), argValue)) {
2883 info.GetReturnValue().SetNull();
2884 return;
2885 }
2886
2887 double nRate = ValueToDouble(info.GetIsolate(), argValue);
2888 if (nRate <= 0) {
2889 pContext->ThrowArgumentMismatchException();
2890 return;
2891 }
2892
2893 std::vector<double> data;
2894 for (int32_t i = 1; i < argc; i++) {
2895 argValue = GetSimpleValue(info, i);
2896 if (ValueIsNull(info.GetIsolate(), argValue)) {
2897 info.GetReturnValue().SetNull();
2898 return;
2899 }
2900 data.push_back(ValueToDouble(info.GetIsolate(), argValue));
2901 }
2902
2903 double nSum = 0;
2904 double nDivisor = 1.0 + nRate;
2905 while (!data.empty()) {
2906 nSum += data.back();
2907 nSum /= nDivisor;
2908 data.pop_back();
2909 }
2910 info.GetReturnValue().Set(nSum);
2911 }
2912
2913 // static
Pmt(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2914 void CFXJSE_FormCalcContext::Pmt(
2915 CFXJSE_HostObject* pThis,
2916 const v8::FunctionCallbackInfo<v8::Value>& info) {
2917 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
2918 if (info.Length() != 3) {
2919 pContext->ThrowParamCountMismatchException("Pmt");
2920 return;
2921 }
2922
2923 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
2924 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
2925 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
2926 if (ValueIsNull(info.GetIsolate(), argOne) ||
2927 ValueIsNull(info.GetIsolate(), argTwo) ||
2928 ValueIsNull(info.GetIsolate(), argThree)) {
2929 info.GetReturnValue().SetNull();
2930 return;
2931 }
2932
2933 double nPrincipal = ValueToDouble(info.GetIsolate(), argOne);
2934 double nRate = ValueToDouble(info.GetIsolate(), argTwo);
2935 int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
2936 if (nPrincipal <= 0 || nRate <= 0 || nPeriods == 0) {
2937 pContext->ThrowArgumentMismatchException();
2938 return;
2939 }
2940
2941 double nSum = pow(1.0 + nRate, nPeriods);
2942 info.GetReturnValue().Set((nPrincipal * nRate * nSum) / (nSum - 1));
2943 }
2944
2945 // static
PPmt(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)2946 void CFXJSE_FormCalcContext::PPmt(
2947 CFXJSE_HostObject* pThis,
2948 const v8::FunctionCallbackInfo<v8::Value>& info) {
2949 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
2950 if (info.Length() != 5) {
2951 pContext->ThrowParamCountMismatchException("PPmt");
2952 return;
2953 }
2954
2955 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
2956 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
2957 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
2958 v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
2959 v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
2960 if (ValueIsNull(info.GetIsolate(), argOne) ||
2961 ValueIsNull(info.GetIsolate(), argTwo) ||
2962 ValueIsNull(info.GetIsolate(), argThree) ||
2963 ValueIsNull(info.GetIsolate(), argFour) ||
2964 ValueIsNull(info.GetIsolate(), argFive)) {
2965 info.GetReturnValue().SetNull();
2966 return;
2967 }
2968
2969 float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne);
2970 float nRate = ValueToFloat(info.GetIsolate(), argTwo);
2971 float nPayment = ValueToFloat(info.GetIsolate(), argThree);
2972 float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour);
2973 float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive);
2974 if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
2975 (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
2976 pContext->ThrowArgumentMismatchException();
2977 return;
2978 }
2979
2980 float nRateOfMonth = nRate / 12;
2981 int32_t iNums = static_cast<int32_t>(
2982 (log10((float)(nPayment / nPrincipalAmount)) -
2983 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
2984 log10((float)(1 + nRateOfMonth)));
2985 int32_t iEnd =
2986 std::min(static_cast<int32_t>(nFirstMonth + nNumberOfMonths - 1), iNums);
2987 if (nPayment < nPrincipalAmount * nRateOfMonth) {
2988 pContext->ThrowArgumentMismatchException();
2989 return;
2990 }
2991
2992 int32_t i = 0;
2993 for (i = 0; i < nFirstMonth - 1; ++i)
2994 nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
2995
2996 float nTemp = 0;
2997 float nSum = 0;
2998 for (; i < iEnd; ++i) {
2999 nTemp = nPayment - nPrincipalAmount * nRateOfMonth;
3000 nSum += nTemp;
3001 nPrincipalAmount -= nTemp;
3002 }
3003 info.GetReturnValue().Set(nSum);
3004 }
3005
3006 // static
PV(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3007 void CFXJSE_FormCalcContext::PV(
3008 CFXJSE_HostObject* pThis,
3009 const v8::FunctionCallbackInfo<v8::Value>& info) {
3010 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
3011 if (info.Length() != 3) {
3012 pContext->ThrowParamCountMismatchException("PV");
3013 return;
3014 }
3015
3016 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3017 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3018 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
3019 if (ValueIsNull(info.GetIsolate(), argOne) ||
3020 ValueIsNull(info.GetIsolate(), argTwo) ||
3021 ValueIsNull(info.GetIsolate(), argThree)) {
3022 info.GetReturnValue().SetNull();
3023 return;
3024 }
3025
3026 double nAmount = ValueToDouble(info.GetIsolate(), argOne);
3027 double nRate = ValueToDouble(info.GetIsolate(), argTwo);
3028 int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
3029 if (nAmount <= 0 || nRate < 0 || nPeriods == 0) {
3030 pContext->ThrowArgumentMismatchException();
3031 return;
3032 }
3033
3034 double nTemp = 1 / pow(1.0 + nRate, nPeriods);
3035 info.GetReturnValue().Set(nAmount * ((1.0 - nTemp) / nRate));
3036 }
3037
3038 // static
Rate(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3039 void CFXJSE_FormCalcContext::Rate(
3040 CFXJSE_HostObject* pThis,
3041 const v8::FunctionCallbackInfo<v8::Value>& info) {
3042 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
3043 if (info.Length() != 3) {
3044 pContext->ThrowParamCountMismatchException("Rate");
3045 return;
3046 }
3047
3048 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3049 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3050 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
3051 if (ValueIsNull(info.GetIsolate(), argOne) ||
3052 ValueIsNull(info.GetIsolate(), argTwo) ||
3053 ValueIsNull(info.GetIsolate(), argThree)) {
3054 info.GetReturnValue().SetNull();
3055 return;
3056 }
3057
3058 float nFuture = ValueToFloat(info.GetIsolate(), argOne);
3059 float nPresent = ValueToFloat(info.GetIsolate(), argTwo);
3060 int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
3061 if (nFuture <= 0 || nPresent < 0 || nPeriods == 0) {
3062 pContext->ThrowArgumentMismatchException();
3063 return;
3064 }
3065
3066 info.GetReturnValue().Set(powf(nFuture / nPresent, 1.0f / nPeriods) - 1.0f);
3067 }
3068
3069 // static
Term(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3070 void CFXJSE_FormCalcContext::Term(
3071 CFXJSE_HostObject* pThis,
3072 const v8::FunctionCallbackInfo<v8::Value>& info) {
3073 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
3074 if (info.Length() != 3) {
3075 pContext->ThrowParamCountMismatchException("Term");
3076 return;
3077 }
3078
3079 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3080 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3081 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
3082 if (ValueIsNull(info.GetIsolate(), argOne) ||
3083 ValueIsNull(info.GetIsolate(), argTwo) ||
3084 ValueIsNull(info.GetIsolate(), argThree)) {
3085 info.GetReturnValue().SetNull();
3086 return;
3087 }
3088
3089 float nMount = ValueToFloat(info.GetIsolate(), argOne);
3090 float nRate = ValueToFloat(info.GetIsolate(), argTwo);
3091 float nFuture = ValueToFloat(info.GetIsolate(), argThree);
3092 if ((nMount <= 0) || (nRate <= 0) || (nFuture <= 0)) {
3093 pContext->ThrowArgumentMismatchException();
3094 return;
3095 }
3096
3097 info.GetReturnValue().Set(log((float)(nFuture / nMount * nRate) + 1) /
3098 log((float)(1 + nRate)));
3099 }
3100
3101 // static
Choose(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3102 void CFXJSE_FormCalcContext::Choose(
3103 CFXJSE_HostObject* pThis,
3104 const v8::FunctionCallbackInfo<v8::Value>& info) {
3105 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
3106 int32_t argc = info.Length();
3107 if (argc < 2) {
3108 pContext->ThrowParamCountMismatchException("Choose");
3109 return;
3110 }
3111
3112 if (ValueIsNull(info.GetIsolate(), info[0])) {
3113 info.GetReturnValue().SetNull();
3114 return;
3115 }
3116
3117 int32_t iIndex =
3118 static_cast<int32_t>(ValueToFloat(info.GetIsolate(), info[0]));
3119 if (iIndex < 1) {
3120 info.GetReturnValue().SetEmptyString();
3121 return;
3122 }
3123
3124 bool bFound = false;
3125 bool bStopCounterFlags = false;
3126 int32_t iArgIndex = 1;
3127 int32_t iValueIndex = 0;
3128 while (!bFound && !bStopCounterFlags && (iArgIndex < argc)) {
3129 v8::Local<v8::Value> argIndexValue = info[iArgIndex];
3130 if (fxv8::IsArray(argIndexValue)) {
3131 v8::Local<v8::Array> arr = argIndexValue.As<v8::Array>();
3132 uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
3133 if (iLength > 3)
3134 bStopCounterFlags = true;
3135
3136 iValueIndex += (iLength - 2);
3137 if (iValueIndex >= iIndex) {
3138 v8::Local<v8::Value> propertyValue =
3139 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
3140 v8::Local<v8::Value> jsValue = fxv8::ReentrantGetArrayElementHelper(
3141 info.GetIsolate(), arr, (iLength - 1) - (iValueIndex - iIndex));
3142 v8::Local<v8::Value> newPropertyValue;
3143 if (fxv8::IsObject(jsValue)) {
3144 v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
3145 if (fxv8::IsNull(propertyValue)) {
3146 newPropertyValue =
3147 GetObjectDefaultValue(info.GetIsolate(), jsObjectValue);
3148 } else {
3149 ByteString bsName = fxv8::ReentrantToByteStringHelper(
3150 info.GetIsolate(), propertyValue);
3151 newPropertyValue = fxv8::ReentrantGetObjectPropertyHelper(
3152 info.GetIsolate(), jsObjectValue, bsName.AsStringView());
3153 }
3154 }
3155 ByteString bsChosen =
3156 ValueToUTF8String(info.GetIsolate(), newPropertyValue);
3157 info.GetReturnValue().Set(
3158 fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView()));
3159 bFound = true;
3160 }
3161 } else {
3162 iValueIndex++;
3163 if (iValueIndex == iIndex) {
3164 ByteString bsChosen =
3165 ValueToUTF8String(info.GetIsolate(), argIndexValue);
3166 info.GetReturnValue().Set(
3167 fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView()));
3168 bFound = true;
3169 }
3170 }
3171 iArgIndex++;
3172 }
3173 if (!bFound)
3174 info.GetReturnValue().SetEmptyString();
3175 }
3176
3177 // static
Exists(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3178 void CFXJSE_FormCalcContext::Exists(
3179 CFXJSE_HostObject* pThis,
3180 const v8::FunctionCallbackInfo<v8::Value>& info) {
3181 if (info.Length() != 1) {
3182 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Exists");
3183 return;
3184 }
3185 info.GetReturnValue().Set(fxv8::IsObject(info[0]));
3186 }
3187
3188 // static
HasValue(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3189 void CFXJSE_FormCalcContext::HasValue(
3190 CFXJSE_HostObject* pThis,
3191 const v8::FunctionCallbackInfo<v8::Value>& info) {
3192 if (info.Length() != 1) {
3193 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("HasValue");
3194 return;
3195 }
3196
3197 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3198 if (!fxv8::IsString(argOne)) {
3199 info.GetReturnValue().Set(
3200 static_cast<int>(fxv8::IsNumber(argOne) || fxv8::IsBoolean(argOne)));
3201 return;
3202 }
3203
3204 ByteString bsValue =
3205 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argOne);
3206 bsValue.TrimLeft();
3207 info.GetReturnValue().Set(static_cast<int>(!bsValue.IsEmpty()));
3208 }
3209
3210 // static
Oneof(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3211 void CFXJSE_FormCalcContext::Oneof(
3212 CFXJSE_HostObject* pThis,
3213 const v8::FunctionCallbackInfo<v8::Value>& info) {
3214 if (info.Length() < 2) {
3215 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Oneof");
3216 return;
3217 }
3218
3219 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3220 for (const auto& value : UnfoldArgs(info)) {
3221 if (SimpleValueCompare(info.GetIsolate(), argOne, value)) {
3222 info.GetReturnValue().Set(1);
3223 return;
3224 }
3225 }
3226 info.GetReturnValue().Set(0);
3227 }
3228
3229 // static
Within(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3230 void CFXJSE_FormCalcContext::Within(
3231 CFXJSE_HostObject* pThis,
3232 const v8::FunctionCallbackInfo<v8::Value>& info) {
3233 if (info.Length() != 3) {
3234 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Within");
3235 return;
3236 }
3237
3238 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3239 if (fxv8::IsNull(argOne)) {
3240 info.GetReturnValue().SetUndefined();
3241 return;
3242 }
3243
3244 v8::Local<v8::Value> argLow = GetSimpleValue(info, 1);
3245 v8::Local<v8::Value> argHigh = GetSimpleValue(info, 2);
3246 if (fxv8::IsNumber(argOne)) {
3247 float oneNumber = ValueToFloat(info.GetIsolate(), argOne);
3248 float lowNumber = ValueToFloat(info.GetIsolate(), argLow);
3249 float heightNumber = ValueToFloat(info.GetIsolate(), argHigh);
3250 info.GetReturnValue().Set(static_cast<int>((oneNumber >= lowNumber) &&
3251 (oneNumber <= heightNumber)));
3252 return;
3253 }
3254
3255 ByteString bsOne = ValueToUTF8String(info.GetIsolate(), argOne);
3256 ByteString bsLow = ValueToUTF8String(info.GetIsolate(), argLow);
3257 ByteString bsHeight = ValueToUTF8String(info.GetIsolate(), argHigh);
3258 info.GetReturnValue().Set(
3259 static_cast<int>((bsOne.Compare(bsLow.AsStringView()) >= 0) &&
3260 (bsOne.Compare(bsHeight.AsStringView()) <= 0)));
3261 }
3262
3263 // static
If(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3264 void CFXJSE_FormCalcContext::If(
3265 CFXJSE_HostObject* pThis,
3266 const v8::FunctionCallbackInfo<v8::Value>& info) {
3267 if (info.Length() != 3) {
3268 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("If");
3269 return;
3270 }
3271
3272 const bool condition = fxv8::ReentrantToBooleanHelper(
3273 info.GetIsolate(), GetSimpleValue(info, 0));
3274
3275 info.GetReturnValue().Set(GetSimpleValue(info, condition ? 1 : 2));
3276 }
3277
3278 // static
Eval(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3279 void CFXJSE_FormCalcContext::Eval(
3280 CFXJSE_HostObject* pThis,
3281 const v8::FunctionCallbackInfo<v8::Value>& info) {
3282 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
3283 if (info.Length() != 1) {
3284 pContext->ThrowParamCountMismatchException("Eval");
3285 return;
3286 }
3287
3288 v8::Isolate* pIsolate = pContext->GetIsolate();
3289 v8::Local<v8::Value> scriptValue = GetSimpleValue(info, 0);
3290 ByteString bsUtf8Script = ValueToUTF8String(info.GetIsolate(), scriptValue);
3291 if (bsUtf8Script.IsEmpty()) {
3292 info.GetReturnValue().SetNull();
3293 return;
3294 }
3295
3296 WideString wsCalcScript = WideString::FromUTF8(bsUtf8Script.AsStringView());
3297 absl::optional<WideTextBuffer> wsJavaScriptBuf =
3298 CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(),
3299 wsCalcScript.AsStringView());
3300 if (!wsJavaScriptBuf.has_value()) {
3301 pContext->ThrowCompilerErrorException();
3302 return;
3303 }
3304 std::unique_ptr<CFXJSE_Context> pNewContext =
3305 CFXJSE_Context::Create(pIsolate, nullptr, nullptr, nullptr);
3306
3307 auto returnValue = std::make_unique<CFXJSE_Value>();
3308 ByteString bsScript = FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView());
3309 pNewContext->ExecuteScript(bsScript.AsStringView(), returnValue.get(),
3310 v8::Local<v8::Object>());
3311
3312 info.GetReturnValue().Set(returnValue->DirectGetValue());
3313 }
3314
3315 // static
Ref(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3316 void CFXJSE_FormCalcContext::Ref(
3317 CFXJSE_HostObject* pThis,
3318 const v8::FunctionCallbackInfo<v8::Value>& info) {
3319 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
3320 if (info.Length() != 1) {
3321 pContext->ThrowParamCountMismatchException("Ref");
3322 return;
3323 }
3324
3325 v8::Local<v8::Value> argOne = info[0];
3326 if (fxv8::IsBoolean(argOne) || fxv8::IsString(argOne) ||
3327 fxv8::IsNumber(argOne)) {
3328 info.GetReturnValue().Set(argOne);
3329 return;
3330 }
3331
3332 std::vector<v8::Local<v8::Value>> values(3);
3333 int intVal = 3;
3334 if (fxv8::IsNull(argOne)) {
3335 // TODO(dsinclair): Why is this 4 when the others are all 3?
3336 intVal = 4;
3337 values[2] = fxv8::NewNullHelper(info.GetIsolate());
3338 } else if (fxv8::IsArray(argOne)) {
3339 v8::Local<v8::Array> arr = argOne.As<v8::Array>();
3340 v8::Local<v8::Value> propertyValue =
3341 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
3342 v8::Local<v8::Value> jsObjectValue =
3343 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
3344 if (!fxv8::IsNull(propertyValue) || fxv8::IsNull(jsObjectValue)) {
3345 pContext->ThrowArgumentMismatchException();
3346 return;
3347 }
3348 values[2] = jsObjectValue;
3349 } else if (fxv8::IsObject(argOne)) {
3350 values[2] = argOne;
3351 } else {
3352 pContext->ThrowArgumentMismatchException();
3353 return;
3354 }
3355
3356 values[0] = fxv8::NewNumberHelper(info.GetIsolate(), intVal);
3357 values[1] = fxv8::NewNullHelper(info.GetIsolate());
3358 info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values));
3359 }
3360
3361 // static
UnitType(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3362 void CFXJSE_FormCalcContext::UnitType(
3363 CFXJSE_HostObject* pThis,
3364 const v8::FunctionCallbackInfo<v8::Value>& info) {
3365 if (info.Length() != 1) {
3366 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitType");
3367 return;
3368 }
3369
3370 v8::Local<v8::Value> unitspanValue = GetSimpleValue(info, 0);
3371 if (fxv8::IsNull(unitspanValue)) {
3372 info.GetReturnValue().SetNull();
3373 return;
3374 }
3375
3376 ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue);
3377 if (bsUnitspan.IsEmpty()) {
3378 info.GetReturnValue().SetEmptyString();
3379 return;
3380 }
3381
3382 enum XFA_FormCalc_VALUETYPE_ParserStatus {
3383 VALUETYPE_START,
3384 VALUETYPE_HAVEINVALIDCHAR,
3385 VALUETYPE_HAVEDIGIT,
3386 VALUETYPE_HAVEDIGITWHITE,
3387 VALUETYPE_ISCM,
3388 VALUETYPE_ISMM,
3389 VALUETYPE_ISPT,
3390 VALUETYPE_ISMP,
3391 VALUETYPE_ISIN,
3392 };
3393 bsUnitspan.MakeLower();
3394 WideString wsType = WideString::FromUTF8(bsUnitspan.AsStringView());
3395 const wchar_t* pData = wsType.c_str();
3396 int32_t u = 0;
3397 int32_t uLen = wsType.GetLength();
3398 while (IsWhitespace(pData[u]))
3399 u++;
3400
3401 XFA_FormCalc_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START;
3402 wchar_t typeChar;
3403 // TODO(dsinclair): Cleanup this parser, figure out what the various checks
3404 // are for.
3405 while (u < uLen) {
3406 typeChar = pData[u];
3407 if (IsWhitespace(typeChar)) {
3408 if (eParserStatus != VALUETYPE_HAVEDIGIT &&
3409 eParserStatus != VALUETYPE_HAVEDIGITWHITE) {
3410 eParserStatus = VALUETYPE_ISIN;
3411 break;
3412 }
3413 eParserStatus = VALUETYPE_HAVEDIGITWHITE;
3414 } else if (IsPartOfNumberW(typeChar)) {
3415 if (eParserStatus == VALUETYPE_HAVEDIGITWHITE) {
3416 eParserStatus = VALUETYPE_ISIN;
3417 break;
3418 }
3419 eParserStatus = VALUETYPE_HAVEDIGIT;
3420 } else if ((typeChar == 'c' || typeChar == 'p') && (u + 1 < uLen)) {
3421 wchar_t nextChar = pData[u + 1];
3422 if ((eParserStatus == VALUETYPE_START ||
3423 eParserStatus == VALUETYPE_HAVEDIGIT ||
3424 eParserStatus == VALUETYPE_HAVEDIGITWHITE) &&
3425 !IsPartOfNumberW(nextChar)) {
3426 eParserStatus = (typeChar == 'c') ? VALUETYPE_ISCM : VALUETYPE_ISPT;
3427 break;
3428 }
3429 eParserStatus = VALUETYPE_HAVEINVALIDCHAR;
3430 } else if (typeChar == 'm' && (u + 1 < uLen)) {
3431 wchar_t nextChar = pData[u + 1];
3432 if ((eParserStatus == VALUETYPE_START ||
3433 eParserStatus == VALUETYPE_HAVEDIGIT ||
3434 eParserStatus == VALUETYPE_HAVEDIGITWHITE) &&
3435 !IsPartOfNumberW(nextChar)) {
3436 eParserStatus = VALUETYPE_ISMM;
3437 if (nextChar == 'p' || ((u + 5 < uLen) && pData[u + 1] == 'i' &&
3438 pData[u + 2] == 'l' && pData[u + 3] == 'l' &&
3439 pData[u + 4] == 'i' && pData[u + 5] == 'p')) {
3440 eParserStatus = VALUETYPE_ISMP;
3441 }
3442 break;
3443 }
3444 } else {
3445 eParserStatus = VALUETYPE_HAVEINVALIDCHAR;
3446 }
3447 u++;
3448 }
3449 switch (eParserStatus) {
3450 case VALUETYPE_ISCM:
3451 info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "cm"));
3452 break;
3453 case VALUETYPE_ISMM:
3454 info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "mm"));
3455 break;
3456 case VALUETYPE_ISPT:
3457 info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "pt"));
3458 break;
3459 case VALUETYPE_ISMP:
3460 info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "mp"));
3461 break;
3462 default:
3463 info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "in"));
3464 break;
3465 }
3466 }
3467
3468 // static
UnitValue(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3469 void CFXJSE_FormCalcContext::UnitValue(
3470 CFXJSE_HostObject* pThis,
3471 const v8::FunctionCallbackInfo<v8::Value>& info) {
3472 int32_t argc = info.Length();
3473 if (argc < 1 || argc > 2) {
3474 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitValue");
3475 return;
3476 }
3477
3478 v8::Local<v8::Value> unitspanValue = GetSimpleValue(info, 0);
3479 if (fxv8::IsNull(unitspanValue)) {
3480 info.GetReturnValue().SetNull();
3481 return;
3482 }
3483
3484 ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue);
3485 const char* pData = bsUnitspan.c_str();
3486 if (!pData) {
3487 info.GetReturnValue().Set(0);
3488 return;
3489 }
3490
3491 size_t u = 0;
3492 while (IsWhitespace(pData[u]))
3493 ++u;
3494
3495 while (u < bsUnitspan.GetLength()) {
3496 if (!IsPartOfNumber(pData[u]))
3497 break;
3498 ++u;
3499 }
3500
3501 char* pTemp = nullptr;
3502 double dFirstNumber = strtod(pData, &pTemp);
3503 while (IsWhitespace(pData[u]))
3504 ++u;
3505
3506 size_t uLen = bsUnitspan.GetLength();
3507 ByteString bsFirstUnit;
3508 while (u < uLen) {
3509 if (pData[u] == ' ')
3510 break;
3511
3512 bsFirstUnit += pData[u];
3513 ++u;
3514 }
3515 bsFirstUnit.MakeLower();
3516
3517 ByteString bsUnit;
3518 if (argc > 1) {
3519 v8::Local<v8::Value> unitValue = GetSimpleValue(info, 1);
3520 ByteString bsUnitTemp = ValueToUTF8String(info.GetIsolate(), unitValue);
3521 const char* pChar = bsUnitTemp.c_str();
3522 size_t uVal = 0;
3523 while (IsWhitespace(pChar[uVal]))
3524 ++uVal;
3525
3526 while (uVal < bsUnitTemp.GetLength()) {
3527 if (!isdigit(pChar[uVal]) && pChar[uVal] != '.')
3528 break;
3529 ++uVal;
3530 }
3531 while (IsWhitespace(pChar[uVal]))
3532 ++uVal;
3533
3534 size_t uValLen = bsUnitTemp.GetLength();
3535 while (uVal < uValLen) {
3536 if (pChar[uVal] == ' ')
3537 break;
3538
3539 bsUnit += pChar[uVal];
3540 ++uVal;
3541 }
3542 bsUnit.MakeLower();
3543 } else {
3544 bsUnit = bsFirstUnit;
3545 }
3546
3547 double dResult = 0;
3548 if (bsFirstUnit == "in" || bsFirstUnit == "inches") {
3549 if (bsUnit == "mm" || bsUnit == "millimeters")
3550 dResult = dFirstNumber * 25.4;
3551 else if (bsUnit == "cm" || bsUnit == "centimeters")
3552 dResult = dFirstNumber * 2.54;
3553 else if (bsUnit == "pt" || bsUnit == "points")
3554 dResult = dFirstNumber / 72;
3555 else if (bsUnit == "mp" || bsUnit == "millipoints")
3556 dResult = dFirstNumber / 72000;
3557 else
3558 dResult = dFirstNumber;
3559 } else if (bsFirstUnit == "mm" || bsFirstUnit == "millimeters") {
3560 if (bsUnit == "mm" || bsUnit == "millimeters")
3561 dResult = dFirstNumber;
3562 else if (bsUnit == "cm" || bsUnit == "centimeters")
3563 dResult = dFirstNumber / 10;
3564 else if (bsUnit == "pt" || bsUnit == "points")
3565 dResult = dFirstNumber / 25.4 / 72;
3566 else if (bsUnit == "mp" || bsUnit == "millipoints")
3567 dResult = dFirstNumber / 25.4 / 72000;
3568 else
3569 dResult = dFirstNumber / 25.4;
3570 } else if (bsFirstUnit == "cm" || bsFirstUnit == "centimeters") {
3571 if (bsUnit == "mm" || bsUnit == "millimeters")
3572 dResult = dFirstNumber * 10;
3573 else if (bsUnit == "cm" || bsUnit == "centimeters")
3574 dResult = dFirstNumber;
3575 else if (bsUnit == "pt" || bsUnit == "points")
3576 dResult = dFirstNumber / 2.54 / 72;
3577 else if (bsUnit == "mp" || bsUnit == "millipoints")
3578 dResult = dFirstNumber / 2.54 / 72000;
3579 else
3580 dResult = dFirstNumber / 2.54;
3581 } else if (bsFirstUnit == "pt" || bsFirstUnit == "points") {
3582 if (bsUnit == "mm" || bsUnit == "millimeters")
3583 dResult = dFirstNumber / 72 * 25.4;
3584 else if (bsUnit == "cm" || bsUnit == "centimeters")
3585 dResult = dFirstNumber / 72 * 2.54;
3586 else if (bsUnit == "pt" || bsUnit == "points")
3587 dResult = dFirstNumber;
3588 else if (bsUnit == "mp" || bsUnit == "millipoints")
3589 dResult = dFirstNumber * 1000;
3590 else
3591 dResult = dFirstNumber / 72;
3592 } else if (bsFirstUnit == "mp" || bsFirstUnit == "millipoints") {
3593 if (bsUnit == "mm" || bsUnit == "millimeters")
3594 dResult = dFirstNumber / 72000 * 25.4;
3595 else if (bsUnit == "cm" || bsUnit == "centimeters")
3596 dResult = dFirstNumber / 72000 * 2.54;
3597 else if (bsUnit == "pt" || bsUnit == "points")
3598 dResult = dFirstNumber / 1000;
3599 else if (bsUnit == "mp" || bsUnit == "millipoints")
3600 dResult = dFirstNumber;
3601 else
3602 dResult = dFirstNumber / 72000;
3603 }
3604 info.GetReturnValue().Set(dResult);
3605 }
3606
3607 // static
At(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3608 void CFXJSE_FormCalcContext::At(
3609 CFXJSE_HostObject* pThis,
3610 const v8::FunctionCallbackInfo<v8::Value>& info) {
3611 if (info.Length() != 2) {
3612 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("At");
3613 return;
3614 }
3615
3616 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3617 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3618 if (ValueIsNull(info.GetIsolate(), argOne) ||
3619 ValueIsNull(info.GetIsolate(), argTwo)) {
3620 info.GetReturnValue().SetNull();
3621 return;
3622 }
3623
3624 ByteString stringTwo = ValueToUTF8String(info.GetIsolate(), argTwo);
3625 if (stringTwo.IsEmpty()) {
3626 info.GetReturnValue().Set(1);
3627 return;
3628 }
3629
3630 ByteString stringOne = ValueToUTF8String(info.GetIsolate(), argOne);
3631 auto pos = stringOne.Find(stringTwo.AsStringView());
3632 info.GetReturnValue().Set(
3633 static_cast<int>(pos.has_value() ? pos.value() + 1 : 0));
3634 }
3635
3636 // static
Concat(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3637 void CFXJSE_FormCalcContext::Concat(
3638 CFXJSE_HostObject* pThis,
3639 const v8::FunctionCallbackInfo<v8::Value>& info) {
3640 int32_t argc = info.Length();
3641 if (argc < 1) {
3642 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Concat");
3643 return;
3644 }
3645
3646 ByteString bsResult;
3647 bool bAllNull = true;
3648 for (int32_t i = 0; i < argc; i++) {
3649 v8::Local<v8::Value> value = GetSimpleValue(info, i);
3650 if (ValueIsNull(info.GetIsolate(), value))
3651 continue;
3652
3653 bAllNull = false;
3654 bsResult += ValueToUTF8String(info.GetIsolate(), value);
3655 }
3656
3657 if (bAllNull) {
3658 info.GetReturnValue().SetNull();
3659 return;
3660 }
3661 info.GetReturnValue().Set(
3662 fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView()));
3663 }
3664
3665 // static
Decode(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3666 void CFXJSE_FormCalcContext::Decode(
3667 CFXJSE_HostObject* pThis,
3668 const v8::FunctionCallbackInfo<v8::Value>& info) {
3669 int32_t argc = info.Length();
3670 if (argc < 1 || argc > 2) {
3671 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Decode");
3672 return;
3673 }
3674
3675 if (argc == 1) {
3676 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3677 if (ValueIsNull(info.GetIsolate(), argOne)) {
3678 info.GetReturnValue().SetNull();
3679 return;
3680 }
3681
3682 WideString decoded = DecodeURL(WideString::FromUTF8(
3683 ValueToUTF8String(info.GetIsolate(), argOne).AsStringView()));
3684 auto result = FX_UTF8Encode(decoded.AsStringView());
3685 info.GetReturnValue().Set(
3686 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
3687 return;
3688 }
3689
3690 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3691 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3692 if (ValueIsNull(info.GetIsolate(), argOne) ||
3693 ValueIsNull(info.GetIsolate(), argTwo)) {
3694 info.GetReturnValue().SetNull();
3695 return;
3696 }
3697
3698 ByteString bsToDecode = ValueToUTF8String(info.GetIsolate(), argOne);
3699 ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo);
3700 WideString decoded;
3701
3702 WideString wsToDecode = WideString::FromUTF8(bsToDecode.AsStringView());
3703
3704 if (bsIdentify.EqualNoCase("html"))
3705 decoded = DecodeHTML(wsToDecode);
3706 else if (bsIdentify.EqualNoCase("xml"))
3707 decoded = DecodeXML(wsToDecode);
3708 else
3709 decoded = DecodeURL(wsToDecode);
3710
3711 auto result = FX_UTF8Encode(decoded.AsStringView());
3712 info.GetReturnValue().Set(
3713 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
3714 }
3715
3716 // static
Encode(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3717 void CFXJSE_FormCalcContext::Encode(
3718 CFXJSE_HostObject* pThis,
3719 const v8::FunctionCallbackInfo<v8::Value>& info) {
3720 int32_t argc = info.Length();
3721 if (argc < 1 || argc > 2) {
3722 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Encode");
3723 return;
3724 }
3725
3726 if (argc == 1) {
3727 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3728 if (ValueIsNull(info.GetIsolate(), argOne)) {
3729 info.GetReturnValue().SetNull();
3730 return;
3731 }
3732 WideString encoded =
3733 EncodeURL(ValueToUTF8String(info.GetIsolate(), argOne));
3734 auto result = FX_UTF8Encode(encoded.AsStringView());
3735 info.GetReturnValue().Set(
3736 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
3737 return;
3738 }
3739
3740 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3741 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3742 if (ValueIsNull(info.GetIsolate(), argOne) ||
3743 ValueIsNull(info.GetIsolate(), argTwo)) {
3744 info.GetReturnValue().SetNull();
3745 return;
3746 }
3747
3748 ByteString bsToEncode = ValueToUTF8String(info.GetIsolate(), argOne);
3749 ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo);
3750 WideString encoded;
3751 if (bsIdentify.EqualNoCase("html"))
3752 encoded = EncodeHTML(bsToEncode);
3753 else if (bsIdentify.EqualNoCase("xml"))
3754 encoded = EncodeXML(bsToEncode);
3755 else
3756 encoded = EncodeURL(bsToEncode);
3757
3758 auto result = FX_UTF8Encode(encoded.AsStringView());
3759 info.GetReturnValue().Set(
3760 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
3761 }
3762
3763 // static
Format(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3764 void CFXJSE_FormCalcContext::Format(
3765 CFXJSE_HostObject* pThis,
3766 const v8::FunctionCallbackInfo<v8::Value>& info) {
3767 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
3768 if (info.Length() < 2) {
3769 pContext->ThrowParamCountMismatchException("Format");
3770 return;
3771 }
3772
3773 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3774 ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne);
3775
3776 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3777 ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo);
3778
3779 CXFA_Document* pDoc = pContext->GetDocument();
3780 CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
3781 CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
3782 GCedLocaleIface* pLocale = pThisNode->GetLocale();
3783 WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView());
3784 WideString wsValue = WideString::FromUTF8(bsValue.AsStringView());
3785 bool bPatternIsString;
3786 CXFA_LocaleValue::ValueType dwPatternType;
3787 std::tie(bPatternIsString, dwPatternType) =
3788 PatternStringType(bsPattern.AsStringView());
3789 if (!bPatternIsString) {
3790 switch (dwPatternType) {
3791 case CXFA_LocaleValue::ValueType::kDateTime: {
3792 auto iTChar = wsPattern.Find(L'T');
3793 if (!iTChar.has_value()) {
3794 info.GetReturnValue().SetEmptyString();
3795 return;
3796 }
3797 WideString wsDatePattern(L"date{");
3798 wsDatePattern += wsPattern.First(iTChar.value()) + L"} ";
3799
3800 WideString wsTimePattern(L"time{");
3801 wsTimePattern +=
3802 wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}";
3803 wsPattern = wsDatePattern + wsTimePattern;
3804 } break;
3805 case CXFA_LocaleValue::ValueType::kDate: {
3806 wsPattern = L"date{" + wsPattern + L"}";
3807 } break;
3808 case CXFA_LocaleValue::ValueType::kTime: {
3809 wsPattern = L"time{" + wsPattern + L"}";
3810 } break;
3811 case CXFA_LocaleValue::ValueType::kText: {
3812 wsPattern = L"text{" + wsPattern + L"}";
3813 } break;
3814 case CXFA_LocaleValue::ValueType::kFloat: {
3815 wsPattern = L"num{" + wsPattern + L"}";
3816 } break;
3817 default: {
3818 WideString wsTestPattern = L"num{" + wsPattern + L"}";
3819 CXFA_LocaleValue tempLocaleValue(CXFA_LocaleValue::ValueType::kFloat,
3820 wsValue, wsTestPattern, pLocale, pMgr);
3821 if (tempLocaleValue.IsValid()) {
3822 wsPattern = std::move(wsTestPattern);
3823 dwPatternType = CXFA_LocaleValue::ValueType::kFloat;
3824 } else {
3825 wsPattern = L"text{" + wsPattern + L"}";
3826 dwPatternType = CXFA_LocaleValue::ValueType::kText;
3827 }
3828 } break;
3829 }
3830 }
3831 CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
3832 pMgr);
3833 WideString wsRet;
3834 if (!localeValue.FormatPatterns(wsRet, wsPattern, pLocale,
3835 XFA_ValuePicture::kDisplay)) {
3836 info.GetReturnValue().SetEmptyString();
3837 return;
3838 }
3839 info.GetReturnValue().Set(
3840 fxv8::NewStringHelper(info.GetIsolate(), wsRet.ToUTF8().AsStringView()));
3841 }
3842
3843 // static
Left(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3844 void CFXJSE_FormCalcContext::Left(
3845 CFXJSE_HostObject* pThis,
3846 const v8::FunctionCallbackInfo<v8::Value>& info) {
3847 if (info.Length() != 2) {
3848 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Left");
3849 return;
3850 }
3851
3852 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3853 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3854 if ((ValueIsNull(info.GetIsolate(), argOne)) ||
3855 (ValueIsNull(info.GetIsolate(), argTwo))) {
3856 info.GetReturnValue().SetNull();
3857 return;
3858 }
3859
3860 ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
3861 int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo));
3862 info.GetReturnValue().Set(fxv8::NewStringHelper(
3863 info.GetIsolate(), bsSource.First(count).AsStringView()));
3864 }
3865
3866 // static
Len(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3867 void CFXJSE_FormCalcContext::Len(
3868 CFXJSE_HostObject* pThis,
3869 const v8::FunctionCallbackInfo<v8::Value>& info) {
3870 if (info.Length() != 1) {
3871 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Len");
3872 return;
3873 }
3874
3875 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3876 if (ValueIsNull(info.GetIsolate(), argOne)) {
3877 info.GetReturnValue().SetNull();
3878 return;
3879 }
3880
3881 ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
3882 info.GetReturnValue().Set(static_cast<int>(bsSource.GetLength()));
3883 }
3884
3885 // static
Lower(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3886 void CFXJSE_FormCalcContext::Lower(
3887 CFXJSE_HostObject* pThis,
3888 const v8::FunctionCallbackInfo<v8::Value>& info) {
3889 int32_t argc = info.Length();
3890 if (argc < 1 || argc > 2) {
3891 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Lower");
3892 return;
3893 }
3894
3895 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3896 if (ValueIsNull(info.GetIsolate(), argOne)) {
3897 info.GetReturnValue().SetNull();
3898 return;
3899 }
3900
3901 WideTextBuffer szLowBuf;
3902 ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
3903 WideString wsArg = WideString::FromUTF8(bsArg.AsStringView());
3904 for (wchar_t ch : wsArg) {
3905 if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0xC0 && ch <= 0xDE))
3906 ch += 32;
3907 else if (ch == 0x100 || ch == 0x102 || ch == 0x104)
3908 ch += 1;
3909 szLowBuf.AppendChar(ch);
3910 }
3911 auto result = FX_UTF8Encode(szLowBuf.AsStringView());
3912 info.GetReturnValue().Set(
3913 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
3914 }
3915
3916 // static
Ltrim(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3917 void CFXJSE_FormCalcContext::Ltrim(
3918 CFXJSE_HostObject* pThis,
3919 const v8::FunctionCallbackInfo<v8::Value>& info) {
3920 if (info.Length() != 1) {
3921 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ltrim");
3922 return;
3923 }
3924
3925 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3926 if (ValueIsNull(info.GetIsolate(), argOne)) {
3927 info.GetReturnValue().SetNull();
3928 return;
3929 }
3930
3931 ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
3932 bsSource.TrimLeft();
3933 info.GetReturnValue().Set(
3934 fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView()));
3935 }
3936
3937 // static
Parse(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)3938 void CFXJSE_FormCalcContext::Parse(
3939 CFXJSE_HostObject* pThis,
3940 const v8::FunctionCallbackInfo<v8::Value>& info) {
3941 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
3942 if (info.Length() != 2) {
3943 pContext->ThrowParamCountMismatchException("Parse");
3944 return;
3945 }
3946
3947 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
3948 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
3949 if (ValueIsNull(info.GetIsolate(), argTwo)) {
3950 info.GetReturnValue().SetNull();
3951 return;
3952 }
3953
3954 ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne);
3955 ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo);
3956 CXFA_Document* pDoc = pContext->GetDocument();
3957 CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
3958 CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
3959 GCedLocaleIface* pLocale = pThisNode->GetLocale();
3960 WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView());
3961 WideString wsValue = WideString::FromUTF8(bsValue.AsStringView());
3962 bool bPatternIsString;
3963 CXFA_LocaleValue::ValueType dwPatternType;
3964 std::tie(bPatternIsString, dwPatternType) =
3965 PatternStringType(bsPattern.AsStringView());
3966 if (bPatternIsString) {
3967 CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
3968 pMgr);
3969 if (!localeValue.IsValid()) {
3970 info.GetReturnValue().SetEmptyString();
3971 return;
3972 }
3973 auto result = localeValue.GetValue().ToUTF8();
3974 info.GetReturnValue().Set(
3975 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
3976 return;
3977 }
3978
3979 switch (dwPatternType) {
3980 case CXFA_LocaleValue::ValueType::kDateTime: {
3981 auto iTChar = wsPattern.Find(L'T');
3982 if (!iTChar.has_value()) {
3983 info.GetReturnValue().SetEmptyString();
3984 return;
3985 }
3986 WideString wsDatePattern(L"date{" + wsPattern.First(iTChar.value()) +
3987 L"} ");
3988 WideString wsTimePattern(
3989 L"time{" +
3990 wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}");
3991 wsPattern = wsDatePattern + wsTimePattern;
3992 CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
3993 pMgr);
3994 if (!localeValue.IsValid()) {
3995 info.GetReturnValue().SetEmptyString();
3996 return;
3997 }
3998 auto result = localeValue.GetValue().ToUTF8();
3999 info.GetReturnValue().Set(
4000 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
4001 return;
4002 }
4003 case CXFA_LocaleValue::ValueType::kDate: {
4004 wsPattern = L"date{" + wsPattern + L"}";
4005 CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
4006 pMgr);
4007 if (!localeValue.IsValid()) {
4008 info.GetReturnValue().SetEmptyString();
4009 return;
4010 }
4011 auto result = localeValue.GetValue().ToUTF8();
4012 info.GetReturnValue().Set(
4013 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
4014 return;
4015 }
4016 case CXFA_LocaleValue::ValueType::kTime: {
4017 wsPattern = L"time{" + wsPattern + L"}";
4018 CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
4019 pMgr);
4020 if (!localeValue.IsValid()) {
4021 info.GetReturnValue().SetEmptyString();
4022 return;
4023 }
4024 auto result = localeValue.GetValue().ToUTF8();
4025 info.GetReturnValue().Set(
4026 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
4027 return;
4028 }
4029 case CXFA_LocaleValue::ValueType::kText: {
4030 wsPattern = L"text{" + wsPattern + L"}";
4031 CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText, wsValue,
4032 wsPattern, pLocale, pMgr);
4033 if (!localeValue.IsValid()) {
4034 info.GetReturnValue().SetEmptyString();
4035 return;
4036 }
4037 auto result = localeValue.GetValue().ToUTF8();
4038 info.GetReturnValue().Set(
4039 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
4040 return;
4041 }
4042 case CXFA_LocaleValue::ValueType::kFloat: {
4043 wsPattern = L"num{" + wsPattern + L"}";
4044 CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat, wsValue,
4045 wsPattern, pLocale, pMgr);
4046 if (!localeValue.IsValid()) {
4047 info.GetReturnValue().SetEmptyString();
4048 return;
4049 }
4050 info.GetReturnValue().Set(localeValue.GetDoubleNum());
4051 return;
4052 }
4053 default: {
4054 {
4055 WideString wsTestPattern = L"num{" + wsPattern + L"}";
4056 CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat,
4057 wsValue, wsTestPattern, pLocale, pMgr);
4058 if (localeValue.IsValid()) {
4059 info.GetReturnValue().Set(localeValue.GetDoubleNum());
4060 return;
4061 }
4062 }
4063
4064 {
4065 WideString wsTestPattern = L"text{" + wsPattern + L"}";
4066 CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText,
4067 wsValue, wsTestPattern, pLocale, pMgr);
4068 if (localeValue.IsValid()) {
4069 auto result = localeValue.GetValue().ToUTF8();
4070 info.GetReturnValue().Set(
4071 fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
4072 return;
4073 }
4074 }
4075 info.GetReturnValue().SetEmptyString();
4076 return;
4077 }
4078 }
4079 }
4080
4081 // static
Replace(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4082 void CFXJSE_FormCalcContext::Replace(
4083 CFXJSE_HostObject* pThis,
4084 const v8::FunctionCallbackInfo<v8::Value>& info) {
4085 int32_t argc = info.Length();
4086 if (argc < 2 || argc > 3) {
4087 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Replace");
4088 return;
4089 }
4090
4091 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4092 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
4093 ByteString bsOne;
4094 ByteString bsTwo;
4095 if (!ValueIsNull(info.GetIsolate(), argOne) &&
4096 !ValueIsNull(info.GetIsolate(), argTwo)) {
4097 bsOne = ValueToUTF8String(info.GetIsolate(), argOne);
4098 bsTwo = ValueToUTF8String(info.GetIsolate(), argTwo);
4099 }
4100
4101 ByteString bsThree;
4102 if (argc > 2) {
4103 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
4104 bsThree = ValueToUTF8String(info.GetIsolate(), argThree);
4105 }
4106
4107 bsOne.Replace(bsTwo.AsStringView(), bsThree.AsStringView());
4108 info.GetReturnValue().Set(
4109 fxv8::NewStringHelper(info.GetIsolate(), bsOne.AsStringView()));
4110 }
4111
4112 // static
Right(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4113 void CFXJSE_FormCalcContext::Right(
4114 CFXJSE_HostObject* pThis,
4115 const v8::FunctionCallbackInfo<v8::Value>& info) {
4116 if (info.Length() != 2) {
4117 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Right");
4118 return;
4119 }
4120
4121 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4122 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
4123 if ((ValueIsNull(info.GetIsolate(), argOne)) ||
4124 (ValueIsNull(info.GetIsolate(), argTwo))) {
4125 info.GetReturnValue().SetNull();
4126 return;
4127 }
4128
4129 ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
4130 int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo));
4131 info.GetReturnValue().Set(fxv8::NewStringHelper(
4132 info.GetIsolate(), bsSource.Last(count).AsStringView()));
4133 }
4134
4135 // static
Rtrim(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4136 void CFXJSE_FormCalcContext::Rtrim(
4137 CFXJSE_HostObject* pThis,
4138 const v8::FunctionCallbackInfo<v8::Value>& info) {
4139 if (info.Length() != 1) {
4140 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Rtrim");
4141 return;
4142 }
4143
4144 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4145 if (ValueIsNull(info.GetIsolate(), argOne)) {
4146 info.GetReturnValue().SetNull();
4147 return;
4148 }
4149
4150 ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
4151 bsSource.TrimRight();
4152 info.GetReturnValue().Set(
4153 fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView()));
4154 }
4155
4156 // static
Space(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4157 void CFXJSE_FormCalcContext::Space(
4158 CFXJSE_HostObject* pThis,
4159 const v8::FunctionCallbackInfo<v8::Value>& info) {
4160 if (info.Length() != 1) {
4161 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Space");
4162 return;
4163 }
4164
4165 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4166 if (fxv8::IsNull(argOne)) {
4167 info.GetReturnValue().SetNull();
4168 return;
4169 }
4170
4171 int count = std::max(0, ValueToInteger(info.GetIsolate(), argOne));
4172 if (count > kMaxCharCount) {
4173 ToFormCalcContext(pThis)->ThrowException("String too long.");
4174 return;
4175 }
4176 DataVector<char> space_string(count, ' ');
4177 info.GetReturnValue().Set(
4178 fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(space_string)));
4179 }
4180
4181 // static
Str(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4182 void CFXJSE_FormCalcContext::Str(
4183 CFXJSE_HostObject* pThis,
4184 const v8::FunctionCallbackInfo<v8::Value>& info) {
4185 int32_t argc = info.Length();
4186 if (argc < 1 || argc > 3) {
4187 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Str");
4188 return;
4189 }
4190
4191 v8::Local<v8::Value> numberValue = GetSimpleValue(info, 0);
4192 if (fxv8::IsNull(numberValue)) {
4193 info.GetReturnValue().SetNull();
4194 return;
4195 }
4196 float fNumber = ValueToFloat(info.GetIsolate(), numberValue);
4197
4198 constexpr int32_t kDefaultWidth = 10;
4199 int32_t iWidth = kDefaultWidth;
4200 if (argc > 1) {
4201 v8::Local<v8::Value> widthValue = GetSimpleValue(info, 1);
4202 iWidth = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), widthValue));
4203 if (iWidth > kMaxCharCount) {
4204 ToFormCalcContext(pThis)->ThrowException("String too long.");
4205 return;
4206 }
4207 }
4208
4209 constexpr int32_t kDefaultPrecision = 0;
4210 int32_t iPrecision = kDefaultPrecision;
4211 if (argc > 2) {
4212 constexpr int32_t kMaxPrecision = 15;
4213 v8::Local<v8::Value> precision_value = GetSimpleValue(info, 2);
4214 iPrecision = std::max(0, static_cast<int32_t>(ValueToFloat(
4215 info.GetIsolate(), precision_value)));
4216 iPrecision = std::min(iPrecision, kMaxPrecision);
4217 }
4218
4219 ByteString bsFormat = "%";
4220 if (iPrecision) {
4221 bsFormat += ".";
4222 bsFormat += ByteString::FormatInteger(iPrecision);
4223 }
4224 bsFormat += "f";
4225 ByteString bsNumber = ByteString::Format(bsFormat.c_str(), fNumber);
4226
4227 const char* pData = bsNumber.c_str();
4228 int32_t iLength = bsNumber.GetLength();
4229 int32_t u = 0;
4230 while (u < iLength) {
4231 if (pData[u] == '.')
4232 break;
4233
4234 ++u;
4235 }
4236
4237 if (u > iWidth || (iPrecision + u) >= iWidth) {
4238 DataVector<char> stars(std::max(iWidth, 0), '*');
4239 info.GetReturnValue().Set(
4240 fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(stars)));
4241 return;
4242 }
4243
4244 ByteString resultBuf;
4245 if (u == iLength) {
4246 if (iLength > iWidth) {
4247 int32_t i = 0;
4248 while (i < iWidth) {
4249 resultBuf += '*';
4250 ++i;
4251 }
4252 } else {
4253 int32_t i = 0;
4254 while (i < iWidth - iLength) {
4255 resultBuf += ' ';
4256 ++i;
4257 }
4258 resultBuf += pData;
4259 }
4260 info.GetReturnValue().Set(
4261 fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView()));
4262 return;
4263 }
4264
4265 int32_t iLeavingSpace = iWidth - u - iPrecision;
4266 if (iPrecision != 0)
4267 iLeavingSpace--;
4268
4269 int32_t i = 0;
4270 while (i < iLeavingSpace) {
4271 resultBuf += ' ';
4272 ++i;
4273 }
4274 i = 0;
4275 while (i < u) {
4276 resultBuf += pData[i];
4277 ++i;
4278 }
4279 if (iPrecision != 0)
4280 resultBuf += '.';
4281
4282 u++;
4283 i = 0;
4284 while (u < iLength) {
4285 if (i >= iPrecision)
4286 break;
4287
4288 resultBuf += pData[u];
4289 ++i;
4290 ++u;
4291 }
4292 while (i < iPrecision) {
4293 resultBuf += '0';
4294 ++i;
4295 }
4296 info.GetReturnValue().Set(
4297 fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView()));
4298 }
4299
4300 // static
Stuff(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4301 void CFXJSE_FormCalcContext::Stuff(
4302 CFXJSE_HostObject* pThis,
4303 const v8::FunctionCallbackInfo<v8::Value>& info) {
4304 int32_t argc = info.Length();
4305 if (argc < 3 || argc > 4) {
4306 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Stuff");
4307 return;
4308 }
4309
4310 v8::Local<v8::Value> sourceValue = GetSimpleValue(info, 0);
4311 v8::Local<v8::Value> startValue = GetSimpleValue(info, 1);
4312 v8::Local<v8::Value> deleteValue = GetSimpleValue(info, 2);
4313 if (fxv8::IsNull(sourceValue) || fxv8::IsNull(startValue) ||
4314 fxv8::IsNull(deleteValue)) {
4315 info.GetReturnValue().SetNull();
4316 return;
4317 }
4318
4319 int32_t iStart = 1; // one-based character indexing.
4320 int32_t iDelete = 0;
4321 ByteString bsSource = ValueToUTF8String(info.GetIsolate(), sourceValue);
4322 int32_t iLength = pdfium::base::checked_cast<int32_t>(bsSource.GetLength());
4323 if (iLength) {
4324 iStart = pdfium::clamp(
4325 static_cast<int32_t>(ValueToFloat(info.GetIsolate(), startValue)), 1,
4326 iLength);
4327 iDelete = pdfium::clamp(
4328 static_cast<int32_t>(ValueToFloat(info.GetIsolate(), deleteValue)), 0,
4329 iLength - iStart + 1);
4330 }
4331
4332 ByteString bsInsert;
4333 if (argc > 3) {
4334 v8::Local<v8::Value> insertValue = GetSimpleValue(info, 3);
4335 bsInsert = ValueToUTF8String(info.GetIsolate(), insertValue);
4336 }
4337
4338 --iStart; // now zero-based.
4339 ByteString bsResult = {bsSource.AsStringView().First(iStart),
4340 bsInsert.AsStringView(),
4341 bsSource.AsStringView().Substr(iStart + iDelete)};
4342 info.GetReturnValue().Set(
4343 fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView()));
4344 }
4345
4346 // static
Substr(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4347 void CFXJSE_FormCalcContext::Substr(
4348 CFXJSE_HostObject* pThis,
4349 const v8::FunctionCallbackInfo<v8::Value>& info) {
4350 if (info.Length() != 3) {
4351 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Substr");
4352 return;
4353 }
4354
4355 v8::Local<v8::Value> string_value = GetSimpleValue(info, 0);
4356 v8::Local<v8::Value> start_value = GetSimpleValue(info, 1);
4357 v8::Local<v8::Value> end_value = GetSimpleValue(info, 2);
4358 if (ValueIsNull(info.GetIsolate(), string_value) ||
4359 ValueIsNull(info.GetIsolate(), start_value) ||
4360 ValueIsNull(info.GetIsolate(), end_value)) {
4361 info.GetReturnValue().SetNull();
4362 return;
4363 }
4364
4365 ByteString bsSource = ValueToUTF8String(info.GetIsolate(), string_value);
4366 size_t iLength = bsSource.GetLength();
4367 if (iLength == 0) {
4368 info.GetReturnValue().SetEmptyString();
4369 return;
4370 }
4371
4372 // |start_value| is 1-based. Assume first character if |start_value| is less
4373 // than 1, per spec. Subtract 1 since |iStart| is 0-based.
4374 size_t iStart =
4375 std::max(ValueToInteger(info.GetIsolate(), start_value), 1) - 1;
4376 if (iStart >= iLength) {
4377 info.GetReturnValue().SetEmptyString();
4378 return;
4379 }
4380
4381 // Negative values are treated as 0. Can't clamp() due to sign mismatches.
4382 size_t iCount = std::max(ValueToInteger(info.GetIsolate(), end_value), 0);
4383 iCount = std::min(iCount, iLength - iStart);
4384 info.GetReturnValue().Set(fxv8::NewStringHelper(
4385 info.GetIsolate(), bsSource.Substr(iStart, iCount).AsStringView()));
4386 }
4387
4388 // static
Uuid(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4389 void CFXJSE_FormCalcContext::Uuid(
4390 CFXJSE_HostObject* pThis,
4391 const v8::FunctionCallbackInfo<v8::Value>& info) {
4392 int32_t argc = info.Length();
4393 if (argc < 0 || argc > 1) {
4394 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Uuid");
4395 return;
4396 }
4397
4398 int32_t iNum = 0;
4399 if (argc > 0) {
4400 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4401 iNum = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), argOne));
4402 }
4403 info.GetReturnValue().Set(fxv8::NewStringHelper(
4404 info.GetIsolate(), GUIDString(!!iNum).AsStringView()));
4405 }
4406
4407 // static
Upper(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4408 void CFXJSE_FormCalcContext::Upper(
4409 CFXJSE_HostObject* pThis,
4410 const v8::FunctionCallbackInfo<v8::Value>& info) {
4411 int32_t argc = info.Length();
4412 if (argc < 1 || argc > 2) {
4413 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Upper");
4414 return;
4415 }
4416
4417 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4418 if (ValueIsNull(info.GetIsolate(), argOne)) {
4419 info.GetReturnValue().SetNull();
4420 return;
4421 }
4422
4423 ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
4424 WideString wsArg = WideString::FromUTF8(bsArg.AsStringView());
4425 WideString upperStringBuf;
4426 upperStringBuf.Reserve(wsArg.GetLength());
4427 for (wchar_t ch : wsArg) {
4428 if ((ch >= 0x61 && ch <= 0x7A) || (ch >= 0xE0 && ch <= 0xFE))
4429 ch -= 32;
4430 else if (ch == 0x101 || ch == 0x103 || ch == 0x105)
4431 ch -= 1;
4432
4433 upperStringBuf += ch;
4434 }
4435 info.GetReturnValue().Set(fxv8::NewStringHelper(
4436 info.GetIsolate(),
4437 FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView()));
4438 }
4439
4440 // static
WordNum(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4441 void CFXJSE_FormCalcContext::WordNum(
4442 CFXJSE_HostObject* pThis,
4443 const v8::FunctionCallbackInfo<v8::Value>& info) {
4444 int32_t argc = info.Length();
4445 if (argc < 1 || argc > 3) {
4446 ToFormCalcContext(pThis)->ThrowParamCountMismatchException("WordNum");
4447 return;
4448 }
4449
4450 v8::Local<v8::Value> numberValue = GetSimpleValue(info, 0);
4451 if (fxv8::IsNull(numberValue)) {
4452 info.GetReturnValue().SetNull();
4453 return;
4454 }
4455 float fNumber = ValueToFloat(info.GetIsolate(), numberValue);
4456
4457 int32_t iIdentifier = 0;
4458 if (argc > 1) {
4459 v8::Local<v8::Value> identifierValue = GetSimpleValue(info, 1);
4460 if (fxv8::IsNull(identifierValue)) {
4461 info.GetReturnValue().SetNull();
4462 return;
4463 }
4464 iIdentifier =
4465 static_cast<int32_t>(ValueToFloat(info.GetIsolate(), identifierValue));
4466 }
4467
4468 ByteString bsLocale;
4469 if (argc > 2) {
4470 v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
4471 if (fxv8::IsNull(localeValue)) {
4472 info.GetReturnValue().SetNull();
4473 return;
4474 }
4475 bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
4476 }
4477
4478 if (isnan(fNumber) || fNumber < 0.0f || fNumber > 922337203685477550.0f) {
4479 info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "*"));
4480 return;
4481 }
4482 ByteString bsFormatted = ByteString::Format("%.2f", fNumber);
4483 ByteString bsWorded = WordUS(bsFormatted.AsStringView(), iIdentifier);
4484 info.GetReturnValue().Set(
4485 fxv8::NewStringHelper(info.GetIsolate(), bsWorded.AsStringView()));
4486 }
4487
4488 // static
Get(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4489 void CFXJSE_FormCalcContext::Get(
4490 CFXJSE_HostObject* pThis,
4491 const v8::FunctionCallbackInfo<v8::Value>& info) {
4492 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
4493 if (info.Length() != 1) {
4494 pContext->ThrowParamCountMismatchException("Get");
4495 return;
4496 }
4497
4498 CXFA_Document* pDoc = pContext->GetDocument();
4499 if (!pDoc)
4500 return;
4501
4502 CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
4503 if (!pAppProvider)
4504 return;
4505
4506 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4507 ByteString bsUrl = ValueToUTF8String(info.GetIsolate(), argOne);
4508 RetainPtr<IFX_SeekableReadStream> pFile =
4509 pAppProvider->DownloadURL(WideString::FromUTF8(bsUrl.AsStringView()));
4510 if (!pFile)
4511 return;
4512
4513 FX_FILESIZE size = pFile->GetSize();
4514 DataVector<uint8_t> dataBuf(size);
4515
4516 // TODO(tsepez): check return value?
4517 (void)pFile->ReadBlock(dataBuf);
4518 info.GetReturnValue().Set(
4519 fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(dataBuf)));
4520 }
4521
4522 // static
Post(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4523 void CFXJSE_FormCalcContext::Post(
4524 CFXJSE_HostObject* pThis,
4525 const v8::FunctionCallbackInfo<v8::Value>& info) {
4526 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
4527 int32_t argc = info.Length();
4528 if (argc < 2 || argc > 5) {
4529 pContext->ThrowParamCountMismatchException("Post");
4530 return;
4531 }
4532
4533 CXFA_Document* pDoc = pContext->GetDocument();
4534 if (!pDoc)
4535 return;
4536
4537 CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
4538 if (!pAppProvider)
4539 return;
4540
4541 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4542 ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne);
4543
4544 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
4545 ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo);
4546
4547 ByteString bsContentType;
4548 if (argc > 2) {
4549 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
4550 bsContentType = ValueToUTF8String(info.GetIsolate(), argThree);
4551 }
4552
4553 ByteString bsEncode;
4554 if (argc > 3) {
4555 v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
4556 bsEncode = ValueToUTF8String(info.GetIsolate(), argFour);
4557 }
4558
4559 ByteString bsHeader;
4560 if (argc > 4) {
4561 v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
4562 bsHeader = ValueToUTF8String(info.GetIsolate(), argFive);
4563 }
4564
4565 WideString decodedResponse;
4566 if (!pAppProvider->PostRequestURL(
4567 WideString::FromUTF8(bsURL.AsStringView()),
4568 WideString::FromUTF8(bsData.AsStringView()),
4569 WideString::FromUTF8(bsContentType.AsStringView()),
4570 WideString::FromUTF8(bsEncode.AsStringView()),
4571 WideString::FromUTF8(bsHeader.AsStringView()), decodedResponse)) {
4572 pContext->ThrowServerDeniedException();
4573 return;
4574 }
4575 info.GetReturnValue().Set(fxv8::NewStringHelper(
4576 info.GetIsolate(), decodedResponse.ToUTF8().AsStringView()));
4577 }
4578
4579 // static
Put(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4580 void CFXJSE_FormCalcContext::Put(
4581 CFXJSE_HostObject* pThis,
4582 const v8::FunctionCallbackInfo<v8::Value>& info) {
4583 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
4584 int32_t argc = info.Length();
4585 if (argc < 2 || argc > 3) {
4586 pContext->ThrowParamCountMismatchException("Put");
4587 return;
4588 }
4589
4590 CXFA_Document* pDoc = pContext->GetDocument();
4591 if (!pDoc)
4592 return;
4593
4594 CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
4595 if (!pAppProvider)
4596 return;
4597
4598 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
4599 ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne);
4600
4601 v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
4602 ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo);
4603
4604 ByteString bsEncode;
4605 if (argc > 2) {
4606 v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
4607 bsEncode = ValueToUTF8String(info.GetIsolate(), argThree);
4608 }
4609 if (!pAppProvider->PutRequestURL(
4610 WideString::FromUTF8(bsURL.AsStringView()),
4611 WideString::FromUTF8(bsData.AsStringView()),
4612 WideString::FromUTF8(bsEncode.AsStringView()))) {
4613 pContext->ThrowServerDeniedException();
4614 return;
4615 }
4616 info.GetReturnValue().SetEmptyString();
4617 }
4618
4619 // static
assign_value_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4620 void CFXJSE_FormCalcContext::assign_value_operator(
4621 CFXJSE_HostObject* pThis,
4622 const v8::FunctionCallbackInfo<v8::Value>& info) {
4623 v8::Isolate* pIsolate = info.GetIsolate();
4624 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
4625 if (info.Length() != 2) {
4626 pContext->ThrowCompilerErrorException();
4627 return;
4628 }
4629 ByteStringView bsFuncName("asgn_val_op");
4630 v8::Local<v8::Value> lValue = info[0];
4631 v8::Local<v8::Value> rValue = GetSimpleValue(info, 1);
4632 if (fxv8::IsArray(lValue)) {
4633 v8::Local<v8::Array> arr = lValue.As<v8::Array>();
4634 uint32_t iLeftLength = fxv8::GetArrayLengthHelper(arr);
4635 v8::Local<v8::Value> propertyValue =
4636 fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
4637 for (uint32_t i = 2; i < iLeftLength; i++) {
4638 v8::Local<v8::Value> jsValue =
4639 fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, i);
4640 if (!fxv8::IsObject(jsValue)) {
4641 pContext->ThrowNoDefaultPropertyException(bsFuncName);
4642 return;
4643 }
4644 v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
4645 if (fxv8::IsNull(propertyValue)) {
4646 if (!SetObjectDefaultValue(pIsolate, jsObjectValue, rValue)) {
4647 pContext->ThrowNoDefaultPropertyException(bsFuncName);
4648 return;
4649 }
4650 } else {
4651 fxv8::ReentrantPutObjectPropertyHelper(
4652 pIsolate, jsObjectValue,
4653 fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue)
4654 .AsStringView(),
4655 rValue);
4656 }
4657 }
4658 } else if (fxv8::IsObject(lValue)) {
4659 if (!SetObjectDefaultValue(pIsolate, lValue.As<v8::Object>(), rValue)) {
4660 pContext->ThrowNoDefaultPropertyException(bsFuncName);
4661 return;
4662 }
4663 }
4664 info.GetReturnValue().Set(rValue);
4665 }
4666
4667 // static
logical_or_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4668 void CFXJSE_FormCalcContext::logical_or_operator(
4669 CFXJSE_HostObject* pThis,
4670 const v8::FunctionCallbackInfo<v8::Value>& info) {
4671 if (info.Length() != 2) {
4672 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4673 return;
4674 }
4675
4676 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4677 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4678 if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
4679 info.GetReturnValue().SetNull();
4680 return;
4681 }
4682
4683 float first = ValueToFloat(info.GetIsolate(), argFirst);
4684 float second = ValueToFloat(info.GetIsolate(), argSecond);
4685 info.GetReturnValue().Set(static_cast<int>(first || second));
4686 }
4687
4688 // static
logical_and_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4689 void CFXJSE_FormCalcContext::logical_and_operator(
4690 CFXJSE_HostObject* pThis,
4691 const v8::FunctionCallbackInfo<v8::Value>& info) {
4692 if (info.Length() != 2) {
4693 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4694 return;
4695 }
4696
4697 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4698 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4699 if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
4700 info.GetReturnValue().SetNull();
4701 return;
4702 }
4703
4704 float first = ValueToFloat(info.GetIsolate(), argFirst);
4705 float second = ValueToFloat(info.GetIsolate(), argSecond);
4706 info.GetReturnValue().Set(static_cast<int>(first && second));
4707 }
4708
4709 // static
equality_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4710 void CFXJSE_FormCalcContext::equality_operator(
4711 CFXJSE_HostObject* pThis,
4712 const v8::FunctionCallbackInfo<v8::Value>& info) {
4713 if (info.Length() != 2) {
4714 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4715 return;
4716 }
4717
4718 if (fm_ref_equal(pThis, info)) {
4719 info.GetReturnValue().Set(1);
4720 return;
4721 }
4722
4723 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4724 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4725 if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
4726 info.GetReturnValue().Set(
4727 static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
4728 return;
4729 }
4730
4731 if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
4732 info.GetReturnValue().Set(static_cast<int>(
4733 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) ==
4734 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond)));
4735 return;
4736 }
4737
4738 double first = ValueToDouble(info.GetIsolate(), argFirst);
4739 double second = ValueToDouble(info.GetIsolate(), argSecond);
4740 info.GetReturnValue().Set(static_cast<int>(first == second));
4741 }
4742
4743 // static
notequality_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4744 void CFXJSE_FormCalcContext::notequality_operator(
4745 CFXJSE_HostObject* pThis,
4746 const v8::FunctionCallbackInfo<v8::Value>& info) {
4747 if (info.Length() != 2) {
4748 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4749 return;
4750 }
4751
4752 if (fm_ref_equal(pThis, info)) {
4753 info.GetReturnValue().Set(0);
4754 return;
4755 }
4756
4757 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4758 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4759 if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
4760 info.GetReturnValue().Set(
4761 static_cast<int>(!fxv8::IsNull(argFirst) || !fxv8::IsNull(argSecond)));
4762 return;
4763 }
4764
4765 if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
4766 info.GetReturnValue().Set(static_cast<int>(
4767 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) !=
4768 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond)));
4769 return;
4770 }
4771
4772 double first = ValueToDouble(info.GetIsolate(), argFirst);
4773 double second = ValueToDouble(info.GetIsolate(), argSecond);
4774 info.GetReturnValue().Set(static_cast<int>(first != second));
4775 }
4776
4777 // static
fm_ref_equal(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4778 bool CFXJSE_FormCalcContext::fm_ref_equal(
4779 CFXJSE_HostObject* pThis,
4780 const v8::FunctionCallbackInfo<v8::Value>& info) {
4781 v8::Local<v8::Value> argFirst = info[0];
4782 v8::Local<v8::Value> argSecond = info[1];
4783 if (!fxv8::IsArray(argFirst) || !fxv8::IsArray(argSecond))
4784 return false;
4785
4786 v8::Local<v8::Array> firstArr = argFirst.As<v8::Array>();
4787 v8::Local<v8::Array> secondArr = argSecond.As<v8::Array>();
4788 v8::Local<v8::Value> firstFlag =
4789 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 0);
4790 v8::Local<v8::Value> secondFlag =
4791 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 0);
4792 if (fxv8::ReentrantToInt32Helper(info.GetIsolate(), firstFlag) != 3 ||
4793 fxv8::ReentrantToInt32Helper(info.GetIsolate(), secondFlag) != 3) {
4794 return false;
4795 }
4796
4797 v8::Local<v8::Value> firstValue =
4798 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 2);
4799 v8::Local<v8::Value> secondValue =
4800 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 2);
4801
4802 if (fxv8::IsNull(firstValue) || fxv8::IsNull(secondValue))
4803 return false;
4804
4805 return FXJSE_RetrieveObjectBinding(firstValue) ==
4806 FXJSE_RetrieveObjectBinding(secondValue);
4807 }
4808
4809 // static
less_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4810 void CFXJSE_FormCalcContext::less_operator(
4811 CFXJSE_HostObject* pThis,
4812 const v8::FunctionCallbackInfo<v8::Value>& info) {
4813 if (info.Length() != 2) {
4814 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4815 return;
4816 }
4817
4818 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4819 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4820 if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
4821 info.GetReturnValue().Set(0);
4822 return;
4823 }
4824
4825 if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
4826 ByteString bs1 =
4827 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
4828 ByteString bs2 =
4829 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
4830 info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) < 0);
4831 return;
4832 }
4833
4834 double first = ValueToDouble(info.GetIsolate(), argFirst);
4835 double second = ValueToDouble(info.GetIsolate(), argSecond);
4836 info.GetReturnValue().Set(static_cast<int>(first < second));
4837 }
4838
4839 // static
lessequal_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4840 void CFXJSE_FormCalcContext::lessequal_operator(
4841 CFXJSE_HostObject* pThis,
4842 const v8::FunctionCallbackInfo<v8::Value>& info) {
4843 if (info.Length() != 2) {
4844 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4845 return;
4846 }
4847
4848 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4849 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4850 if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
4851 info.GetReturnValue().Set(
4852 static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
4853 return;
4854 }
4855
4856 if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
4857 auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
4858 auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
4859 info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) <= 0);
4860 return;
4861 }
4862
4863 double first = ValueToDouble(info.GetIsolate(), argFirst);
4864 double second = ValueToDouble(info.GetIsolate(), argSecond);
4865 info.GetReturnValue().Set(static_cast<int>(first <= second));
4866 }
4867
4868 // static
greater_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4869 void CFXJSE_FormCalcContext::greater_operator(
4870 CFXJSE_HostObject* pThis,
4871 const v8::FunctionCallbackInfo<v8::Value>& info) {
4872 if (info.Length() != 2) {
4873 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4874 return;
4875 }
4876
4877 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4878 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4879 if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
4880 info.GetReturnValue().Set(0);
4881 return;
4882 }
4883
4884 if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
4885 auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
4886 auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
4887 info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) > 0);
4888 return;
4889 }
4890
4891 double first = ValueToDouble(info.GetIsolate(), argFirst);
4892 double second = ValueToDouble(info.GetIsolate(), argSecond);
4893 info.GetReturnValue().Set(static_cast<int>(first > second));
4894 }
4895
4896 // static
greaterequal_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4897 void CFXJSE_FormCalcContext::greaterequal_operator(
4898 CFXJSE_HostObject* pThis,
4899 const v8::FunctionCallbackInfo<v8::Value>& info) {
4900 if (info.Length() != 2) {
4901 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4902 return;
4903 }
4904
4905 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4906 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4907 if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
4908 info.GetReturnValue().Set(
4909 static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
4910 return;
4911 }
4912
4913 if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
4914 auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
4915 auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
4916 info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) >= 0);
4917 return;
4918 }
4919
4920 double first = ValueToDouble(info.GetIsolate(), argFirst);
4921 double second = ValueToDouble(info.GetIsolate(), argSecond);
4922 info.GetReturnValue().Set(static_cast<int>(first >= second));
4923 }
4924
4925 // static
plus_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4926 void CFXJSE_FormCalcContext::plus_operator(
4927 CFXJSE_HostObject* pThis,
4928 const v8::FunctionCallbackInfo<v8::Value>& info) {
4929 if (info.Length() != 2) {
4930 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4931 return;
4932 }
4933
4934 if (ValueIsNull(info.GetIsolate(), info[0]) &&
4935 ValueIsNull(info.GetIsolate(), info[1])) {
4936 info.GetReturnValue().SetNull();
4937 return;
4938 }
4939
4940 const double first = ValueToDouble(info.GetIsolate(), info[0]);
4941 const double second = ValueToDouble(info.GetIsolate(), info[1]);
4942 info.GetReturnValue().Set(first + second);
4943 }
4944
4945 // static
minus_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4946 void CFXJSE_FormCalcContext::minus_operator(
4947 CFXJSE_HostObject* pThis,
4948 const v8::FunctionCallbackInfo<v8::Value>& info) {
4949 if (info.Length() != 2) {
4950 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4951 return;
4952 }
4953
4954 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4955 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4956 if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
4957 info.GetReturnValue().SetNull();
4958 return;
4959 }
4960
4961 double first = ValueToDouble(info.GetIsolate(), argFirst);
4962 double second = ValueToDouble(info.GetIsolate(), argSecond);
4963 info.GetReturnValue().Set(first - second);
4964 }
4965
4966 // static
multiple_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4967 void CFXJSE_FormCalcContext::multiple_operator(
4968 CFXJSE_HostObject* pThis,
4969 const v8::FunctionCallbackInfo<v8::Value>& info) {
4970 if (info.Length() != 2) {
4971 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
4972 return;
4973 }
4974
4975 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4976 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4977 if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
4978 info.GetReturnValue().SetNull();
4979 return;
4980 }
4981
4982 double first = ValueToDouble(info.GetIsolate(), argFirst);
4983 double second = ValueToDouble(info.GetIsolate(), argSecond);
4984 info.GetReturnValue().Set(first * second);
4985 }
4986
4987 // static
divide_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)4988 void CFXJSE_FormCalcContext::divide_operator(
4989 CFXJSE_HostObject* pThis,
4990 const v8::FunctionCallbackInfo<v8::Value>& info) {
4991 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
4992 if (info.Length() != 2) {
4993 pContext->ThrowCompilerErrorException();
4994 return;
4995 }
4996
4997 v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
4998 v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
4999 if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
5000 info.GetReturnValue().SetNull();
5001 return;
5002 }
5003
5004 double second = ValueToDouble(info.GetIsolate(), argSecond);
5005 if (second == 0.0) {
5006 pContext->ThrowDivideByZeroException();
5007 return;
5008 }
5009
5010 double first = ValueToDouble(info.GetIsolate(), argFirst);
5011 info.GetReturnValue().Set(first / second);
5012 }
5013
5014 // static
positive_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5015 void CFXJSE_FormCalcContext::positive_operator(
5016 CFXJSE_HostObject* pThis,
5017 const v8::FunctionCallbackInfo<v8::Value>& info) {
5018 if (info.Length() != 1) {
5019 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
5020 return;
5021 }
5022
5023 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
5024 if (fxv8::IsNull(argOne)) {
5025 info.GetReturnValue().SetNull();
5026 return;
5027 }
5028 info.GetReturnValue().Set(0.0 + ValueToDouble(info.GetIsolate(), argOne));
5029 }
5030
5031 // static
negative_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5032 void CFXJSE_FormCalcContext::negative_operator(
5033 CFXJSE_HostObject* pThis,
5034 const v8::FunctionCallbackInfo<v8::Value>& info) {
5035 if (info.Length() != 1) {
5036 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
5037 return;
5038 }
5039
5040 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
5041 if (fxv8::IsNull(argOne)) {
5042 info.GetReturnValue().SetNull();
5043 return;
5044 }
5045 info.GetReturnValue().Set(0.0 - ValueToDouble(info.GetIsolate(), argOne));
5046 }
5047
5048 // static
logical_not_operator(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5049 void CFXJSE_FormCalcContext::logical_not_operator(
5050 CFXJSE_HostObject* pThis,
5051 const v8::FunctionCallbackInfo<v8::Value>& info) {
5052 if (info.Length() != 1) {
5053 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
5054 return;
5055 }
5056
5057 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
5058 if (fxv8::IsNull(argOne)) {
5059 info.GetReturnValue().SetNull();
5060 return;
5061 }
5062
5063 double first = ValueToDouble(info.GetIsolate(), argOne);
5064 info.GetReturnValue().Set((first == 0.0) ? 1 : 0);
5065 }
5066
5067 // static
dot_accessor(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5068 void CFXJSE_FormCalcContext::dot_accessor(
5069 CFXJSE_HostObject* pThis,
5070 const v8::FunctionCallbackInfo<v8::Value>& info) {
5071 DotAccessorCommon(pThis, info, /*bDotAccessor=*/true);
5072 }
5073
5074 // static
dotdot_accessor(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5075 void CFXJSE_FormCalcContext::dotdot_accessor(
5076 CFXJSE_HostObject* pThis,
5077 const v8::FunctionCallbackInfo<v8::Value>& info) {
5078 DotAccessorCommon(pThis, info, /*bDotAccessor=*/false);
5079 }
5080
5081 // static
eval_translation(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5082 void CFXJSE_FormCalcContext::eval_translation(
5083 CFXJSE_HostObject* pThis,
5084 const v8::FunctionCallbackInfo<v8::Value>& info) {
5085 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
5086 if (info.Length() != 1) {
5087 pContext->ThrowParamCountMismatchException("Eval");
5088 return;
5089 }
5090
5091 v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
5092 ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
5093 if (bsArg.IsEmpty()) {
5094 pContext->ThrowArgumentMismatchException();
5095 return;
5096 }
5097
5098 WideString wsCalcScript = WideString::FromUTF8(bsArg.AsStringView());
5099 absl::optional<WideTextBuffer> wsJavaScriptBuf =
5100 CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(),
5101 wsCalcScript.AsStringView());
5102 if (!wsJavaScriptBuf.has_value()) {
5103 pContext->ThrowCompilerErrorException();
5104 return;
5105 }
5106 info.GetReturnValue().Set(fxv8::NewStringHelper(
5107 info.GetIsolate(),
5108 FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView()).AsStringView()));
5109 }
5110
5111 // static
is_fm_object(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5112 void CFXJSE_FormCalcContext::is_fm_object(
5113 CFXJSE_HostObject* pThis,
5114 const v8::FunctionCallbackInfo<v8::Value>& info) {
5115 const bool result = info.Length() == 1 && fxv8::IsObject(info[0]);
5116 info.GetReturnValue().Set(result);
5117 }
5118
5119 // static
is_fm_array(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5120 void CFXJSE_FormCalcContext::is_fm_array(
5121 CFXJSE_HostObject* pThis,
5122 const v8::FunctionCallbackInfo<v8::Value>& info) {
5123 const bool result = info.Length() == 1 && fxv8::IsArray(info[0]);
5124 info.GetReturnValue().Set(result);
5125 }
5126
5127 // static
get_fm_value(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5128 void CFXJSE_FormCalcContext::get_fm_value(
5129 CFXJSE_HostObject* pThis,
5130 const v8::FunctionCallbackInfo<v8::Value>& info) {
5131 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
5132 if (info.Length() != 1) {
5133 pContext->ThrowCompilerErrorException();
5134 return;
5135 }
5136
5137 v8::Local<v8::Value> argOne = info[0];
5138 if (fxv8::IsArray(argOne)) {
5139 v8::Local<v8::Array> arr = argOne.As<v8::Array>();
5140 v8::Local<v8::Value> propertyValue =
5141 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
5142 v8::Local<v8::Value> jsValue =
5143 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
5144 if (!fxv8::IsObject(jsValue)) {
5145 info.GetReturnValue().Set(fxv8::NewUndefinedHelper(info.GetIsolate()));
5146 return;
5147 }
5148 v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
5149 if (fxv8::IsNull(propertyValue)) {
5150 info.GetReturnValue().Set(
5151 GetObjectDefaultValue(info.GetIsolate(), jsObjectValue));
5152 return;
5153 }
5154 ByteString bsName =
5155 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), propertyValue);
5156 info.GetReturnValue().Set(fxv8::ReentrantGetObjectPropertyHelper(
5157 info.GetIsolate(), jsObjectValue, bsName.AsStringView()));
5158 return;
5159 }
5160
5161 if (fxv8::IsObject(argOne)) {
5162 v8::Local<v8::Object> obj = argOne.As<v8::Object>();
5163 info.GetReturnValue().Set(GetObjectDefaultValue(info.GetIsolate(), obj));
5164 return;
5165 }
5166
5167 info.GetReturnValue().Set(argOne);
5168 }
5169
5170 // static
get_fm_jsobj(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5171 void CFXJSE_FormCalcContext::get_fm_jsobj(
5172 CFXJSE_HostObject* pThis,
5173 const v8::FunctionCallbackInfo<v8::Value>& info) {
5174 if (info.Length() != 1) {
5175 ToFormCalcContext(pThis)->ThrowCompilerErrorException();
5176 return;
5177 }
5178
5179 v8::Local<v8::Value> argOne = info[0];
5180 if (!fxv8::IsArray(argOne)) {
5181 info.GetReturnValue().Set(argOne);
5182 return;
5183 }
5184
5185 v8::Local<v8::Array> arr = argOne.As<v8::Array>();
5186 info.GetReturnValue().Set(
5187 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2));
5188 }
5189
5190 // static
fm_var_filter(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5191 void CFXJSE_FormCalcContext::fm_var_filter(
5192 CFXJSE_HostObject* pThis,
5193 const v8::FunctionCallbackInfo<v8::Value>& info) {
5194 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
5195 if (info.Length() != 1) {
5196 pContext->ThrowCompilerErrorException();
5197 return;
5198 }
5199
5200 v8::Local<v8::Value> argOne = info[0];
5201 if (!fxv8::IsArray(argOne)) {
5202 info.GetReturnValue().Set(GetSimpleValue(info, 0));
5203 return;
5204 }
5205
5206 v8::Local<v8::Array> arr = argOne.As<v8::Array>();
5207 v8::Local<v8::Value> flagsValue =
5208 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 0);
5209 int32_t iFlags = fxv8::ReentrantToInt32Helper(info.GetIsolate(), flagsValue);
5210 if (iFlags != 3 && iFlags != 4) {
5211 info.GetReturnValue().Set(GetSimpleValue(info, 0));
5212 return;
5213 }
5214
5215 if (iFlags == 4) {
5216 std::vector<v8::Local<v8::Value>> values(3);
5217 values[0] = fxv8::NewNumberHelper(info.GetIsolate(), 3);
5218 values[1] = fxv8::NewNullHelper(info.GetIsolate());
5219 values[2] = fxv8::NewNullHelper(info.GetIsolate());
5220 info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values));
5221 return;
5222 }
5223
5224 v8::Local<v8::Value> objectValue =
5225 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
5226 if (fxv8::IsNull(objectValue)) {
5227 pContext->ThrowCompilerErrorException();
5228 return;
5229 }
5230 info.GetReturnValue().Set(argOne);
5231 }
5232
5233 // static
concat_fm_object(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info)5234 void CFXJSE_FormCalcContext::concat_fm_object(
5235 CFXJSE_HostObject* pThis,
5236 const v8::FunctionCallbackInfo<v8::Value>& info) {
5237 v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetIsolate();
5238 std::vector<v8::Local<v8::Value>> returnValues;
5239 for (int i = 0; i < info.Length(); ++i) {
5240 if (fxv8::IsArray(info[i])) {
5241 v8::Local<v8::Array> arr = info[i].As<v8::Array>();
5242 uint32_t length = fxv8::GetArrayLengthHelper(arr);
5243 for (uint32_t j = 2; j < length; j++) {
5244 returnValues.push_back(
5245 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, j));
5246 }
5247 }
5248 returnValues.push_back(info[i]);
5249 }
5250 info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, returnValues));
5251 }
5252
5253 // static
GenerateSomExpression(ByteStringView bsName,int32_t iIndexFlags,int32_t iIndexValue,bool bIsStar)5254 ByteString CFXJSE_FormCalcContext::GenerateSomExpression(ByteStringView bsName,
5255 int32_t iIndexFlags,
5256 int32_t iIndexValue,
5257 bool bIsStar) {
5258 if (bIsStar)
5259 return ByteString(bsName, "[*]");
5260
5261 // `iIndexFlags` values are the same as enum class
5262 // `CXFA_FMIndexExpression::AccessorIndex` values.
5263 if (iIndexFlags == 0)
5264 return ByteString(bsName);
5265
5266 if (iIndexFlags == 1 || iIndexValue == 0) {
5267 return ByteString(bsName, "[") + ByteString::FormatInteger(iIndexValue) +
5268 "]";
5269 }
5270
5271 const bool bNegative = iIndexValue < 0;
5272 ByteString bsSomExp(bsName);
5273 if (iIndexFlags == 2) {
5274 bsSomExp += bNegative ? "[-" : "[+";
5275 } else {
5276 DCHECK_EQ(iIndexFlags, 3);
5277 bsSomExp += bNegative ? "[" : "[-";
5278 }
5279
5280 FX_SAFE_INT32 safe_index = iIndexValue;
5281 if (bNegative)
5282 safe_index = -safe_index;
5283 bsSomExp += ByteString::FormatInteger(safe_index.ValueOrDefault(0));
5284 bsSomExp += "]";
5285 return bsSomExp;
5286 }
5287
Translate(cppgc::Heap * pHeap,WideStringView wsFormcalc)5288 absl::optional<WideTextBuffer> CFXJSE_FormCalcContext::Translate(
5289 cppgc::Heap* pHeap,
5290 WideStringView wsFormcalc) {
5291 if (wsFormcalc.IsEmpty())
5292 return WideTextBuffer();
5293
5294 CXFA_FMLexer lexer(wsFormcalc);
5295 CXFA_FMParser parser(pHeap, &lexer);
5296 CXFA_FMAST* ast = parser.Parse();
5297 if (!ast || parser.HasError())
5298 return absl::nullopt;
5299
5300 CXFA_FMToJavaScriptDepth::Reset();
5301 absl::optional<WideTextBuffer> wsJavaScript = ast->ToJavaScript();
5302 if (!wsJavaScript.has_value())
5303 return absl::nullopt;
5304
5305 if (CXFA_IsTooBig(wsJavaScript.value()))
5306 return absl::nullopt;
5307
5308 return wsJavaScript;
5309 }
5310
CFXJSE_FormCalcContext(v8::Isolate * pIsolate,CFXJSE_Context * pScriptContext,CXFA_Document * pDoc)5311 CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pIsolate,
5312 CFXJSE_Context* pScriptContext,
5313 CXFA_Document* pDoc)
5314 : m_pIsolate(pIsolate), m_pDocument(pDoc) {
5315 m_Value.Reset(m_pIsolate,
5316 NewBoundV8Object(
5317 m_pIsolate, CFXJSE_Class::Create(
5318 pScriptContext, &kFormCalcDescriptor, false)
5319 ->GetTemplate(m_pIsolate)));
5320 }
5321
5322 CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() = default;
5323
AsFormCalcContext()5324 CFXJSE_FormCalcContext* CFXJSE_FormCalcContext::AsFormCalcContext() {
5325 return this;
5326 }
5327
GlobalPropertyGetter()5328 v8::Local<v8::Value> CFXJSE_FormCalcContext::GlobalPropertyGetter() {
5329 return v8::Local<v8::Value>::New(m_pIsolate, m_Value);
5330 }
5331
5332 // static
DotAccessorCommon(CFXJSE_HostObject * pThis,const v8::FunctionCallbackInfo<v8::Value> & info,bool bDotAccessor)5333 void CFXJSE_FormCalcContext::DotAccessorCommon(
5334 CFXJSE_HostObject* pThis,
5335 const v8::FunctionCallbackInfo<v8::Value>& info,
5336 bool bDotAccessor) {
5337 CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
5338 v8::Isolate* pIsolate = pContext->GetIsolate();
5339 int32_t argc = info.Length();
5340 if (argc < 4 || argc > 5) {
5341 pContext->ThrowCompilerErrorException();
5342 return;
5343 }
5344
5345 bool bIsStar = true;
5346 int32_t iIndexValue = 0;
5347 if (argc > 4) {
5348 bIsStar = false;
5349 iIndexValue = ValueToInteger(info.GetIsolate(), info[4]);
5350 }
5351
5352 const ByteString bsName =
5353 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[2]);
5354 const bool bHasNoResolveName = bDotAccessor && bsName.IsEmpty();
5355 ByteString bsSomExp = GenerateSomExpression(
5356 bsName.AsStringView(),
5357 fxv8::ReentrantToInt32Helper(info.GetIsolate(), info[3]), iIndexValue,
5358 bIsStar);
5359
5360 v8::Local<v8::Value> argAccessor = info[0];
5361 if (fxv8::IsArray(argAccessor)) {
5362 v8::Local<v8::Array> arr = argAccessor.As<v8::Array>();
5363 uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
5364 if (iLength < 3) {
5365 pContext->ThrowArgumentMismatchException();
5366 return;
5367 }
5368
5369 std::vector<std::vector<v8::Local<v8::Value>>> resolveValues(iLength - 2);
5370 bool bAttribute = false;
5371 bool bAllEmpty = true;
5372 for (uint32_t i = 2; i < iLength; i++) {
5373 v8::Local<v8::Value> hJSObjValue =
5374 fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, i);
5375 absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
5376 ResolveObjects(pThis, hJSObjValue, bsSomExp.AsStringView(),
5377 bDotAccessor, bHasNoResolveName);
5378 if (maybeResult.has_value()) {
5379 resolveValues[i - 2] = ParseResolveResult(pThis, maybeResult.value(),
5380 hJSObjValue, &bAttribute);
5381 bAllEmpty = bAllEmpty && resolveValues[i - 2].empty();
5382 }
5383 }
5384 if (bAllEmpty) {
5385 pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(),
5386 bsSomExp.AsStringView());
5387 return;
5388 }
5389
5390 std::vector<v8::Local<v8::Value>> values;
5391 values.push_back(fxv8::NewNumberHelper(pIsolate, 1));
5392 values.push_back(
5393 bAttribute ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView())
5394 .As<v8::Value>()
5395 : fxv8::NewNullHelper(pIsolate).As<v8::Value>());
5396 for (uint32_t i = 0; i < iLength - 2; i++) {
5397 for (size_t j = 0; j < resolveValues[i].size(); j++)
5398 values.push_back(resolveValues[i][j]);
5399 }
5400 info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values));
5401 return;
5402 }
5403
5404 absl::optional<CFXJSE_Engine::ResolveResult> maybeResult;
5405 ByteString bsAccessorName =
5406 fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[1]);
5407 if (fxv8::IsObject(argAccessor) ||
5408 (fxv8::IsNull(argAccessor) && bsAccessorName.IsEmpty())) {
5409 maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(),
5410 bDotAccessor, bHasNoResolveName);
5411 } else if (!fxv8::IsObject(argAccessor) && !bsAccessorName.IsEmpty()) {
5412 v8::Local<v8::Value> obj =
5413 GetObjectForName(pThis, bsAccessorName.AsStringView());
5414 if (!obj.IsEmpty()) {
5415 argAccessor = obj;
5416 maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(),
5417 bDotAccessor, bHasNoResolveName);
5418 }
5419 }
5420 if (!maybeResult.has_value()) {
5421 pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(),
5422 bsSomExp.AsStringView());
5423 return;
5424 }
5425
5426 bool bAttribute = false;
5427 std::vector<v8::Local<v8::Value>> resolveValues =
5428 ParseResolveResult(pThis, maybeResult.value(), argAccessor, &bAttribute);
5429
5430 std::vector<v8::Local<v8::Value>> values(resolveValues.size() + 2);
5431 values[0] = fxv8::NewNumberHelper(pIsolate, 1);
5432 values[1] = bAttribute
5433 ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView())
5434 .As<v8::Value>()
5435 : fxv8::NewNullHelper(pIsolate).As<v8::Value>();
5436
5437 for (size_t i = 0; i < resolveValues.size(); i++)
5438 values[i + 2] = resolveValues[i];
5439
5440 info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values));
5441 }
5442
ApplyToExpansion(std::function<void (v8::Isolate *,v8::Local<v8::Value>)> fn,const v8::FunctionCallbackInfo<v8::Value> & info,bool bStrict)5443 bool CFXJSE_FormCalcContext::ApplyToExpansion(
5444 std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
5445 const v8::FunctionCallbackInfo<v8::Value>& info,
5446 bool bStrict) {
5447 v8::Isolate* pIsolate = info.GetIsolate();
5448 for (int32_t i = 0; i < info.Length(); i++) {
5449 v8::Local<v8::Value> argValue = info[i];
5450 if (fxv8::IsArray(argValue)) {
5451 if (!ApplyToArray(pIsolate, fn, argValue.As<v8::Array>()) && bStrict) {
5452 ThrowArgumentMismatchException();
5453 return false;
5454 }
5455 } else if (fxv8::IsObject(argValue)) {
5456 ApplyToObject(pIsolate, fn, argValue.As<v8::Object>());
5457 } else if (!fxv8::IsNull(argValue)) {
5458 fn(pIsolate, argValue);
5459 }
5460 }
5461 return true;
5462 }
5463
ApplyToArray(v8::Isolate * pIsolate,std::function<void (v8::Isolate *,v8::Local<v8::Value>)> fn,v8::Local<v8::Array> pArray)5464 bool CFXJSE_FormCalcContext::ApplyToArray(
5465 v8::Isolate* pIsolate,
5466 std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
5467 v8::Local<v8::Array> pArray) {
5468 uint32_t iLength = fxv8::GetArrayLengthHelper(pArray);
5469 if (iLength < 3)
5470 return false;
5471
5472 v8::Local<v8::Value> propertyValue =
5473 fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, 1);
5474
5475 ByteString bsName;
5476 const bool nullprop = fxv8::IsNull(propertyValue);
5477 if (!nullprop)
5478 bsName = fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
5479
5480 for (uint32_t j = 2; j < iLength; j++) {
5481 v8::Local<v8::Value> jsValue =
5482 fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, j);
5483 if (!fxv8::IsObject(jsValue))
5484 continue;
5485
5486 v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
5487 v8::Local<v8::Value> newPropertyValue =
5488 nullprop ? GetObjectDefaultValue(pIsolate, jsObjectValue)
5489 : fxv8::ReentrantGetObjectPropertyHelper(
5490 pIsolate, jsObjectValue, bsName.AsStringView());
5491 if (!fxv8::IsNull(newPropertyValue))
5492 fn(pIsolate, newPropertyValue);
5493 }
5494 return true;
5495 }
5496
ApplyToObject(v8::Isolate * pIsolate,std::function<void (v8::Isolate *,v8::Local<v8::Value>)> fn,v8::Local<v8::Object> pObject)5497 void CFXJSE_FormCalcContext::ApplyToObject(
5498 v8::Isolate* pIsolate,
5499 std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
5500 v8::Local<v8::Object> pObject) {
5501 v8::Local<v8::Value> newPropertyValue =
5502 GetObjectDefaultValue(pIsolate, pObject);
5503 if (!fxv8::IsNull(newPropertyValue))
5504 fn(pIsolate, newPropertyValue);
5505 }
5506
ThrowNoDefaultPropertyException(ByteStringView name) const5507 void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException(
5508 ByteStringView name) const {
5509 ByteString msg(name);
5510 msg += " doesn't have a default property.";
5511 ThrowException(msg.AsStringView());
5512 }
5513
ThrowCompilerErrorException() const5514 void CFXJSE_FormCalcContext::ThrowCompilerErrorException() const {
5515 ThrowException("Compiler error.");
5516 }
5517
ThrowDivideByZeroException() const5518 void CFXJSE_FormCalcContext::ThrowDivideByZeroException() const {
5519 ThrowException("Divide by zero.");
5520 }
5521
ThrowServerDeniedException() const5522 void CFXJSE_FormCalcContext::ThrowServerDeniedException() const {
5523 ThrowException("Server does not permit operation.");
5524 }
5525
ThrowPropertyNotInObjectException(ByteStringView name,ByteStringView exp) const5526 void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException(
5527 ByteStringView name,
5528 ByteStringView exp) const {
5529 ByteString msg("An attempt was made to reference property '");
5530 msg += name;
5531 msg += "' of a non-object in SOM expression ";
5532 msg += exp;
5533 msg += ".";
5534 ThrowException(msg.AsStringView());
5535 }
5536
ThrowParamCountMismatchException(ByteStringView method) const5537 void CFXJSE_FormCalcContext::ThrowParamCountMismatchException(
5538 ByteStringView method) const {
5539 ByteString msg("Incorrect number of parameters calling method '");
5540 msg += method;
5541 msg += "'.";
5542 ThrowException(msg.AsStringView());
5543 }
5544
ThrowArgumentMismatchException() const5545 void CFXJSE_FormCalcContext::ThrowArgumentMismatchException() const {
5546 ThrowException("Argument mismatch in property or function argument.");
5547 }
5548
ThrowException(ByteStringView str) const5549 void CFXJSE_FormCalcContext::ThrowException(ByteStringView str) const {
5550 DCHECK(!str.IsEmpty());
5551 FXJSE_ThrowMessage(GetIsolate(), str);
5552 }
5553