1 // Copyright 2014 PDFium Authors. All rights reserved.
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/cjs_publicmethods.h"
8
9 #include <algorithm>
10 #include <cmath>
11 #include <cwctype>
12 #include <iomanip>
13 #include <iterator>
14 #include <limits>
15 #include <sstream>
16 #include <string>
17 #include <utility>
18 #include <vector>
19
20 #include "build/build_config.h"
21 #include "core/fpdfdoc/cpdf_formcontrol.h"
22 #include "core/fpdfdoc/cpdf_interactiveform.h"
23 #include "core/fxcrt/fx_extension.h"
24 #include "core/fxge/cfx_color.h"
25 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
26 #include "fpdfsdk/cpdfsdk_interactiveform.h"
27 #include "fxjs/cjs_color.h"
28 #include "fxjs/cjs_event_context.h"
29 #include "fxjs/cjs_eventrecorder.h"
30 #include "fxjs/cjs_field.h"
31 #include "fxjs/cjs_object.h"
32 #include "fxjs/cjs_runtime.h"
33 #include "fxjs/cjs_util.h"
34 #include "fxjs/fx_date_helpers.h"
35 #include "fxjs/js_define.h"
36 #include "fxjs/js_resources.h"
37 #include "third_party/base/optional.h"
38 #include "third_party/base/stl_util.h"
39
40 // static
41 const JSMethodSpec CJS_PublicMethods::GlobalFunctionSpecs[] = {
42 {"AFDate_Format", AFDate_Format_static},
43 {"AFDate_FormatEx", AFDate_FormatEx_static},
44 {"AFDate_Keystroke", AFDate_Keystroke_static},
45 {"AFDate_KeystrokeEx", AFDate_KeystrokeEx_static},
46 {"AFExtractNums", AFExtractNums_static},
47 {"AFMakeNumber", AFMakeNumber_static},
48 {"AFMergeChange", AFMergeChange_static},
49 {"AFNumber_Format", AFNumber_Format_static},
50 {"AFNumber_Keystroke", AFNumber_Keystroke_static},
51 {"AFParseDateEx", AFParseDateEx_static},
52 {"AFPercent_Format", AFPercent_Format_static},
53 {"AFPercent_Keystroke", AFPercent_Keystroke_static},
54 {"AFRange_Validate", AFRange_Validate_static},
55 {"AFSimple", AFSimple_static},
56 {"AFSimple_Calculate", AFSimple_Calculate_static},
57 {"AFSpecial_Format", AFSpecial_Format_static},
58 {"AFSpecial_Keystroke", AFSpecial_Keystroke_static},
59 {"AFSpecial_KeystrokeEx", AFSpecial_KeystrokeEx_static},
60 {"AFTime_Format", AFTime_Format_static},
61 {"AFTime_FormatEx", AFTime_FormatEx_static},
62 {"AFTime_Keystroke", AFTime_Keystroke_static},
63 {"AFTime_KeystrokeEx", AFTime_KeystrokeEx_static},
64 };
65
66 namespace {
67
68 #if !defined(OS_ANDROID)
69 constexpr double kDoubleCorrect = 0.000000000000001;
70 #endif
71
72 constexpr const wchar_t* kDateFormats[] = {L"m/d",
73 L"m/d/yy",
74 L"mm/dd/yy",
75 L"mm/yy",
76 L"d-mmm",
77 L"d-mmm-yy",
78 L"dd-mmm-yy",
79 L"yy-mm-dd",
80 L"mmm-yy",
81 L"mmmm-yy",
82 L"mmm d, yyyy",
83 L"mmmm d, yyyy",
84 L"m/d/yy h:MM tt",
85 L"m/d/yy HH:MM"};
86
87 constexpr const wchar_t* kTimeFormats[] = {L"HH:MM", L"h:MM tt", L"HH:MM:ss",
88 L"h:MM:ss tt"};
89
90 #if defined(__FreeBSD__) || defined(__DragonFly__)
91 /*
92 * cvt.c - IEEE floating point formatting routines for FreeBSD
93 * from GNU libc-4.6.27
94 */
95
96 /*
97 * ap_ecvt converts to decimal
98 * the number of digits is specified by ndigit
99 * decpt is set to the position of the decimal point
100 * sign is set to 0 for positive, 1 for negative
101 */
102
103 #define NDIG 80
104
105 static char *
ap_cvt(double arg,int ndigits,int * decpt,int * sign,int eflag)106 ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag)
107 {
108 register int r2;
109 double fi, fj;
110 register char *p, *p1;
111 static char buf[NDIG];
112
113 if (ndigits >= NDIG - 1)
114 ndigits = NDIG - 2;
115 r2 = 0;
116 *sign = 0;
117 p = &buf[0];
118 if (arg < 0) {
119 *sign = 1;
120 arg = -arg;
121 }
122 arg = modf(arg, &fi);
123 p1 = &buf[NDIG];
124 /*
125 * Do integer part
126 */
127 if (fi != 0) {
128 p1 = &buf[NDIG];
129 while (fi != 0) {
130 fj = modf(fi / 10, &fi);
131 *--p1 = (int) ((fj + .03) * 10) + '0';
132 r2++;
133 }
134 while (p1 < &buf[NDIG])
135 *p++ = *p1++;
136 }
137 else if (arg > 0) {
138 while ((fj = arg * 10) < 1) {
139 arg = fj;
140 r2--;
141 }
142 }
143 p1 = &buf[ndigits];
144 if (eflag == 0)
145 p1 += r2;
146 *decpt = r2;
147 if (p1 < &buf[0]) {
148 buf[0] = '\0';
149 return (buf);
150 }
151 while (p <= p1 && p < &buf[NDIG]) {
152 arg *= 10;
153 arg = modf(arg, &fj);
154 *p++ = (int) fj + '0';
155 }
156 if (p1 >= &buf[NDIG]) {
157 buf[NDIG - 1] = '\0';
158 return (buf);
159 }
160 p = p1;
161 *p1 += 5;
162 while (*p1 > '9') {
163 *p1 = '0';
164 if (p1 > buf)
165 ++ * --p1;
166 else {
167 *p1 = '1';
168 (*decpt)++;
169 if (eflag == 0) {
170 if (p > buf)
171 *p = '0';
172 p++;
173 }
174 }
175 }
176 *p = '\0';
177 return (buf);
178 }
179
180 static char *
fcvt(double arg,int ndigits,int * decpt,int * sign)181 fcvt(double arg, int ndigits, int *decpt, int *sign)
182 {
183 return (ap_cvt(arg, ndigits, decpt, sign, 0));
184 }
185 #endif // defined(__FreeBSD__)
186
187 template <typename T>
StrTrim(const T & str)188 T StrTrim(const T& str) {
189 T result = str;
190 result.Trim(' ');
191 return result;
192 }
193
AlertIfPossible(CJS_EventContext * pContext,const WideString & wsCaller,const WideString & wsMsg)194 void AlertIfPossible(CJS_EventContext* pContext,
195 const WideString& wsCaller,
196 const WideString& wsMsg) {
197 CPDFSDK_FormFillEnvironment* pFormFillEnv = pContext->GetFormFillEnv();
198 if (pFormFillEnv) {
199 pFormFillEnv->JS_appAlert(wsMsg, wsCaller, JSPLATFORM_ALERT_BUTTON_OK,
200 JSPLATFORM_ALERT_ICON_STATUS);
201 }
202 }
203
204 #if !defined(OS_ANDROID)
CalculateString(double dValue,int iDec,int * iDec2,bool * bNegative)205 ByteString CalculateString(double dValue,
206 int iDec,
207 int* iDec2,
208 bool* bNegative) {
209 *bNegative = dValue < 0;
210 if (*bNegative)
211 dValue = -dValue;
212
213 // Make sure the number of precision characters will fit.
214 iDec = std::min(iDec, std::numeric_limits<double>::digits10);
215
216 std::stringstream ss;
217 ss << std::fixed << std::setprecision(iDec) << dValue;
218 std::string value = ss.str();
219 size_t pos = value.find('.');
220 *iDec2 = pos == std::string::npos ? value.size() : static_cast<int>(pos);
221 return ByteString(value.c_str());
222 }
223 #endif
224
CalcMergedString(const CJS_EventRecorder * event,const WideString & value,const WideString & change)225 WideString CalcMergedString(const CJS_EventRecorder* event,
226 const WideString& value,
227 const WideString& change) {
228 WideString prefix = value.First(event->SelStart());
229 WideString postfix;
230 int end = event->SelEnd();
231 if (end >= 0 && static_cast<size_t>(end) < value.GetLength())
232 postfix = value.Last(value.GetLength() - static_cast<size_t>(end));
233 return prefix + change + postfix;
234 }
235
236 template <CJS_Result (*F)(CJS_Runtime*,
237 const std::vector<v8::Local<v8::Value>>&)>
JSGlobalFunc(const char * func_name_string,const v8::FunctionCallbackInfo<v8::Value> & info)238 void JSGlobalFunc(const char* func_name_string,
239 const v8::FunctionCallbackInfo<v8::Value>& info) {
240 CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder());
241 if (!pObj)
242 return;
243
244 CJS_Runtime* pRuntime = pObj->GetRuntime();
245 if (!pRuntime)
246 return;
247
248 std::vector<v8::Local<v8::Value>> parameters;
249 for (int i = 0; i < info.Length(); ++i)
250 parameters.push_back(info[i]);
251
252 CJS_Result result = (*F)(pRuntime, parameters);
253 if (result.HasError()) {
254 pRuntime->Error(
255 JSFormatErrorString(func_name_string, nullptr, result.Error()));
256 return;
257 }
258
259 if (result.HasReturn())
260 info.GetReturnValue().Set(result.Return());
261 }
262
WithinBoundsOrZero(int value,size_t size)263 int WithinBoundsOrZero(int value, size_t size) {
264 return value >= 0 && static_cast<size_t>(value) < size ? value : 0;
265 }
266
ValidStyleOrZero(int style)267 int ValidStyleOrZero(int style) {
268 return WithinBoundsOrZero(style, 4);
269 }
270
IsDigitSeparatorOrDecimalMark(int c)271 bool IsDigitSeparatorOrDecimalMark(int c) {
272 return c == '.' || c == ',';
273 }
274
275 #if !defined(OS_ANDROID)
IsStyleWithDigitSeparator(int style)276 bool IsStyleWithDigitSeparator(int style) {
277 return style == 0 || style == 2;
278 }
279
DigitSeparatorForStyle(int style)280 char DigitSeparatorForStyle(int style) {
281 ASSERT(IsStyleWithDigitSeparator(style));
282 return style == 0 ? ',' : '.';
283 }
284
IsStyleWithApostropheSeparator(int style)285 bool IsStyleWithApostropheSeparator(int style) {
286 return style >= 4;
287 }
288 #endif
289
IsStyleWithCommaDecimalMark(int style)290 bool IsStyleWithCommaDecimalMark(int style) {
291 return style == 2 || style == 3;
292 }
293
DecimalMarkForStyle(int style)294 char DecimalMarkForStyle(int style) {
295 return IsStyleWithCommaDecimalMark(style) ? ',' : '.';
296 }
297
298 #if !defined(OS_ANDROID)
NormalizeDecimalMark(ByteString * str)299 void NormalizeDecimalMark(ByteString* str) {
300 str->Replace(",", ".");
301 }
302 #endif
303
NormalizeDecimalMarkW(WideString * str)304 void NormalizeDecimalMarkW(WideString* str) {
305 str->Replace(L",", L".");
306 }
307
ApplyNamedOperation(const wchar_t * sFunction,double dValue1,double dValue2)308 Optional<double> ApplyNamedOperation(const wchar_t* sFunction,
309 double dValue1,
310 double dValue2) {
311 if (FXSYS_wcsicmp(sFunction, L"AVG") == 0 ||
312 FXSYS_wcsicmp(sFunction, L"SUM") == 0) {
313 return dValue1 + dValue2;
314 }
315 if (FXSYS_wcsicmp(sFunction, L"PRD") == 0)
316 return dValue1 * dValue2;
317 if (FXSYS_wcsicmp(sFunction, L"MIN") == 0)
318 return std::min(dValue1, dValue2);
319 if (FXSYS_wcsicmp(sFunction, L"MAX") == 0)
320 return std::max(dValue1, dValue2);
321 return {};
322 }
323
324 } // namespace
325
326 // static
DefineJSObjects(CFXJS_Engine * pEngine)327 void CJS_PublicMethods::DefineJSObjects(CFXJS_Engine* pEngine) {
328 for (const auto& spec : GlobalFunctionSpecs)
329 pEngine->DefineGlobalMethod(spec.pName, spec.pMethodCall);
330 }
331
332 #define JS_STATIC_GLOBAL_FUN(fun_name) \
333 void CJS_PublicMethods::fun_name##_static( \
334 const v8::FunctionCallbackInfo<v8::Value>& info) { \
335 JSGlobalFunc<fun_name>(#fun_name, info); \
336 }
337
338 JS_STATIC_GLOBAL_FUN(AFNumber_Format)
JS_STATIC_GLOBAL_FUN(AFNumber_Keystroke)339 JS_STATIC_GLOBAL_FUN(AFNumber_Keystroke)
340 JS_STATIC_GLOBAL_FUN(AFPercent_Format)
341 JS_STATIC_GLOBAL_FUN(AFPercent_Keystroke)
342 JS_STATIC_GLOBAL_FUN(AFDate_FormatEx)
343 JS_STATIC_GLOBAL_FUN(AFDate_KeystrokeEx)
344 JS_STATIC_GLOBAL_FUN(AFDate_Format)
345 JS_STATIC_GLOBAL_FUN(AFDate_Keystroke)
346 JS_STATIC_GLOBAL_FUN(AFTime_FormatEx)
347 JS_STATIC_GLOBAL_FUN(AFTime_KeystrokeEx)
348 JS_STATIC_GLOBAL_FUN(AFTime_Format)
349 JS_STATIC_GLOBAL_FUN(AFTime_Keystroke)
350 JS_STATIC_GLOBAL_FUN(AFSpecial_Format)
351 JS_STATIC_GLOBAL_FUN(AFSpecial_Keystroke)
352 JS_STATIC_GLOBAL_FUN(AFSpecial_KeystrokeEx)
353 JS_STATIC_GLOBAL_FUN(AFSimple)
354 JS_STATIC_GLOBAL_FUN(AFMakeNumber)
355 JS_STATIC_GLOBAL_FUN(AFSimple_Calculate)
356 JS_STATIC_GLOBAL_FUN(AFRange_Validate)
357 JS_STATIC_GLOBAL_FUN(AFMergeChange)
358 JS_STATIC_GLOBAL_FUN(AFParseDateEx)
359 JS_STATIC_GLOBAL_FUN(AFExtractNums)
360
361 bool CJS_PublicMethods::IsNumber(const WideString& str) {
362 WideString sTrim = StrTrim(str);
363 const wchar_t* pTrim = sTrim.c_str();
364 const wchar_t* p = pTrim;
365 bool bDot = false;
366 bool bKXJS = false;
367
368 wchar_t c;
369 while ((c = *p) != L'\0') {
370 if (IsDigitSeparatorOrDecimalMark(c)) {
371 if (bDot)
372 return false;
373 bDot = true;
374 } else if (c == L'-' || c == L'+') {
375 if (p != pTrim)
376 return false;
377 } else if (c == L'e' || c == L'E') {
378 if (bKXJS)
379 return false;
380
381 p++;
382 c = *p;
383 if (c != L'+' && c != L'-')
384 return false;
385 bKXJS = true;
386 } else if (!FXSYS_IsDecimalDigit(c)) {
387 return false;
388 }
389 p++;
390 }
391
392 return true;
393 }
394
MaskSatisfied(wchar_t c_Change,wchar_t c_Mask)395 bool CJS_PublicMethods::MaskSatisfied(wchar_t c_Change, wchar_t c_Mask) {
396 switch (c_Mask) {
397 case L'9':
398 return !!FXSYS_IsDecimalDigit(c_Change);
399 case L'A':
400 return isascii(c_Change) && isalpha(c_Change);
401 case L'O':
402 return isascii(c_Change) && isalnum(c_Change);
403 case L'X':
404 return true;
405 default:
406 return (c_Change == c_Mask);
407 }
408 }
409
IsReservedMaskChar(wchar_t ch)410 bool CJS_PublicMethods::IsReservedMaskChar(wchar_t ch) {
411 return ch == L'9' || ch == L'A' || ch == L'O' || ch == L'X';
412 }
413
AF_MakeArrayFromList(CJS_Runtime * pRuntime,v8::Local<v8::Value> val)414 v8::Local<v8::Array> CJS_PublicMethods::AF_MakeArrayFromList(
415 CJS_Runtime* pRuntime,
416 v8::Local<v8::Value> val) {
417 ASSERT(!val.IsEmpty());
418 if (val->IsArray())
419 return pRuntime->ToArray(val);
420
421 ASSERT(val->IsString());
422 WideString wsStr = pRuntime->ToWideString(val);
423 ByteString t = wsStr.ToDefANSI();
424 const char* p = t.c_str();
425
426 int nIndex = 0;
427 v8::Local<v8::Array> StrArray = pRuntime->NewArray();
428 while (*p) {
429 const char* pTemp = strchr(p, ',');
430 if (!pTemp) {
431 pRuntime->PutArrayElement(
432 StrArray, nIndex,
433 pRuntime->NewString(StrTrim(ByteString(p)).AsStringView()));
434 break;
435 }
436
437 pRuntime->PutArrayElement(
438 StrArray, nIndex,
439 pRuntime->NewString(StrTrim(ByteString(p, pTemp - p)).AsStringView()));
440
441 nIndex++;
442 p = ++pTemp;
443 }
444 return StrArray;
445 }
446
ParseDate(const WideString & value,bool * bWrongFormat)447 double CJS_PublicMethods::ParseDate(const WideString& value,
448 bool* bWrongFormat) {
449 double dt = FX_GetDateTime();
450 int nYear = FX_GetYearFromTime(dt);
451 int nMonth = FX_GetMonthFromTime(dt) + 1;
452 int nDay = FX_GetDayFromTime(dt);
453 int nHour = FX_GetHourFromTime(dt);
454 int nMin = FX_GetMinFromTime(dt);
455 int nSec = FX_GetSecFromTime(dt);
456
457 int number[3];
458
459 size_t nSkip = 0;
460 size_t nLen = value.GetLength();
461 size_t nIndex = 0;
462 size_t i = 0;
463 while (i < nLen) {
464 if (nIndex > 2)
465 break;
466
467 wchar_t c = value[i];
468 if (FXSYS_IsDecimalDigit(c)) {
469 number[nIndex++] = FX_ParseStringInteger(value, i, &nSkip, 4);
470 i += nSkip;
471 } else {
472 i++;
473 }
474 }
475
476 if (nIndex == 2) {
477 // TODO(thestig): Should the else case set |bWrongFormat| to true?
478 // case2: month/day
479 // case3: day/month
480 if (FX_IsValidMonth(number[0]) && FX_IsValidDay(number[1])) {
481 nMonth = number[0];
482 nDay = number[1];
483 } else if (FX_IsValidDay(number[0]) && FX_IsValidMonth(number[1])) {
484 nDay = number[0];
485 nMonth = number[1];
486 }
487
488 if (bWrongFormat)
489 *bWrongFormat = false;
490 } else if (nIndex == 3) {
491 // TODO(thestig): Should the else case set |bWrongFormat| to true?
492 // case1: year/month/day
493 // case2: month/day/year
494 // case3: day/month/year
495 if (number[0] > 12 && FX_IsValidMonth(number[1]) &&
496 FX_IsValidDay(number[2])) {
497 nYear = number[0];
498 nMonth = number[1];
499 nDay = number[2];
500 } else if (FX_IsValidMonth(number[0]) && FX_IsValidDay(number[1]) &&
501 number[2] > 31) {
502 nMonth = number[0];
503 nDay = number[1];
504 nYear = number[2];
505 } else if (FX_IsValidDay(number[0]) && FX_IsValidMonth(number[1]) &&
506 number[2] > 31) {
507 nDay = number[0];
508 nMonth = number[1];
509 nYear = number[2];
510 }
511
512 if (bWrongFormat)
513 *bWrongFormat = false;
514 } else {
515 if (bWrongFormat)
516 *bWrongFormat = true;
517 return dt;
518 }
519
520 // TODO(thestig): Should we set |bWrongFormat| to false here too?
521 return JS_DateParse(WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay,
522 nYear, nHour, nMin, nSec));
523 }
524
ParseDateUsingFormat(const WideString & value,const WideString & format,bool * bWrongFormat)525 double CJS_PublicMethods::ParseDateUsingFormat(const WideString& value,
526 const WideString& format,
527 bool* bWrongFormat) {
528 double dRet = std::nan("");
529 fxjs::ConversionStatus status = FX_ParseDateUsingFormat(value, format, &dRet);
530 if (status == fxjs::ConversionStatus::kSuccess)
531 return dRet;
532
533 if (status == fxjs::ConversionStatus::kBadDate) {
534 dRet = JS_DateParse(value);
535 if (!std::isnan(dRet))
536 return dRet;
537 }
538
539 bool bBadFormat = false;
540 dRet = ParseDate(value, &bBadFormat);
541 if (bWrongFormat)
542 *bWrongFormat = bBadFormat;
543
544 return dRet;
545 }
546
PrintDateUsingFormat(double dDate,const WideString & format)547 WideString CJS_PublicMethods::PrintDateUsingFormat(double dDate,
548 const WideString& format) {
549 WideString sRet;
550 WideString sPart;
551
552 int nYear = FX_GetYearFromTime(dDate);
553 int nMonth = FX_GetMonthFromTime(dDate) + 1;
554 int nDay = FX_GetDayFromTime(dDate);
555 int nHour = FX_GetHourFromTime(dDate);
556 int nMin = FX_GetMinFromTime(dDate);
557 int nSec = FX_GetSecFromTime(dDate);
558
559 size_t i = 0;
560 while (i < format.GetLength()) {
561 wchar_t c = format[i];
562 size_t remaining = format.GetLength() - i - 1;
563 sPart.clear();
564 switch (c) {
565 case 'y':
566 case 'm':
567 case 'd':
568 case 'H':
569 case 'h':
570 case 'M':
571 case 's':
572 case 't':
573 if (remaining == 0 || format[i + 1] != c) {
574 switch (c) {
575 case 'y':
576 sPart += c;
577 break;
578 case 'm':
579 sPart = WideString::Format(L"%d", nMonth);
580 break;
581 case 'd':
582 sPart = WideString::Format(L"%d", nDay);
583 break;
584 case 'H':
585 sPart = WideString::Format(L"%d", nHour);
586 break;
587 case 'h':
588 sPart =
589 WideString::Format(L"%d", nHour > 12 ? nHour - 12 : nHour);
590 break;
591 case 'M':
592 sPart = WideString::Format(L"%d", nMin);
593 break;
594 case 's':
595 sPart = WideString::Format(L"%d", nSec);
596 break;
597 case 't':
598 sPart += nHour > 12 ? 'p' : 'a';
599 break;
600 }
601 i++;
602 } else if (remaining == 1 || format[i + 2] != c) {
603 switch (c) {
604 case 'y':
605 sPart = WideString::Format(L"%02d", nYear - (nYear / 100) * 100);
606 break;
607 case 'm':
608 sPart = WideString::Format(L"%02d", nMonth);
609 break;
610 case 'd':
611 sPart = WideString::Format(L"%02d", nDay);
612 break;
613 case 'H':
614 sPart = WideString::Format(L"%02d", nHour);
615 break;
616 case 'h':
617 sPart =
618 WideString::Format(L"%02d", nHour > 12 ? nHour - 12 : nHour);
619 break;
620 case 'M':
621 sPart = WideString::Format(L"%02d", nMin);
622 break;
623 case 's':
624 sPart = WideString::Format(L"%02d", nSec);
625 break;
626 case 't':
627 sPart = nHour > 12 ? L"pm" : L"am";
628 break;
629 }
630 i += 2;
631 } else if (remaining == 2 || format[i + 3] != c) {
632 switch (c) {
633 case 'm':
634 i += 3;
635 if (FX_IsValidMonth(nMonth))
636 sPart += fxjs::kMonths[nMonth - 1];
637 break;
638 default:
639 i += 3;
640 sPart += c;
641 sPart += c;
642 sPart += c;
643 break;
644 }
645 } else if (remaining == 3 || format[i + 4] != c) {
646 switch (c) {
647 case 'y':
648 sPart = WideString::Format(L"%04d", nYear);
649 i += 4;
650 break;
651 case 'm':
652 i += 4;
653 if (FX_IsValidMonth(nMonth))
654 sPart += fxjs::kFullMonths[nMonth - 1];
655 break;
656 default:
657 i += 4;
658 sPart += c;
659 sPart += c;
660 sPart += c;
661 sPart += c;
662 break;
663 }
664 } else {
665 i++;
666 sPart += c;
667 }
668 break;
669 default:
670 i++;
671 sPart += c;
672 break;
673 }
674
675 sRet += sPart;
676 }
677
678 return sRet;
679 }
680
681 // function AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency,
682 // bCurrencyPrepend)
AFNumber_Format(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)683 CJS_Result CJS_PublicMethods::AFNumber_Format(
684 CJS_Runtime* pRuntime,
685 const std::vector<v8::Local<v8::Value>>& params) {
686 #if !defined(OS_ANDROID)
687 if (params.size() != 6)
688 return CJS_Result::Failure(JSMessage::kParamError);
689
690 CJS_EventContext* pEventContext = pRuntime->GetCurrentEventContext();
691 CJS_EventRecorder* pEvent = pEventContext->GetEventRecorder();
692 if (!pEvent->HasValue())
693 return CJS_Result::Failure(WideString::FromASCII("No event handler"));
694
695 WideString& Value = pEvent->Value();
696 ByteString strValue = StrTrim(Value.ToDefANSI());
697 if (strValue.IsEmpty())
698 return CJS_Result::Success();
699
700 int iDec = abs(pRuntime->ToInt32(params[0]));
701 int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1]));
702 int iNegStyle = ValidStyleOrZero(pRuntime->ToInt32(params[2]));
703 // params[3] is iCurrStyle, it's not used.
704 WideString wstrCurrency = pRuntime->ToWideString(params[4]);
705 bool bCurrencyPrepend = pRuntime->ToBoolean(params[5]);
706
707 // Processing decimal places
708 NormalizeDecimalMark(&strValue);
709 double dValue = atof(strValue.c_str());
710 if (iDec > 0)
711 dValue += kDoubleCorrect;
712
713 // Calculating number string
714 bool bNegative;
715 int iDec2;
716 strValue = CalculateString(dValue, iDec, &iDec2, &bNegative);
717 if (strValue.IsEmpty()) {
718 dValue = 0;
719 strValue = CalculateString(dValue, iDec, &iDec2, &bNegative);
720 if (strValue.IsEmpty()) {
721 strValue = "0";
722 iDec2 = 1;
723 }
724 }
725 ASSERT(iDec2 >= 0);
726
727 // Processing separator style
728 if (static_cast<size_t>(iDec2) < strValue.GetLength()) {
729 if (IsStyleWithCommaDecimalMark(iSepStyle))
730 strValue.Replace(".", ",");
731
732 if (iDec2 == 0)
733 strValue.Insert(iDec2, '0');
734 }
735 if (IsStyleWithDigitSeparator(iSepStyle)) {
736 char cSeparator = DigitSeparatorForStyle(iSepStyle);
737 for (int iDecPositive = iDec2 - 3; iDecPositive > 0; iDecPositive -= 3)
738 strValue.Insert(iDecPositive, cSeparator);
739 }
740
741 // Processing currency string
742 Value = WideString::FromDefANSI(strValue.AsStringView());
743 if (bCurrencyPrepend)
744 Value = wstrCurrency + Value;
745 else
746 Value = Value + wstrCurrency;
747
748 // Processing negative style
749 if (bNegative) {
750 if (iNegStyle == 0) {
751 Value.InsertAtFront(L'-');
752 } else if (iNegStyle == 2 || iNegStyle == 3) {
753 Value.InsertAtFront(L'(');
754 Value += L')';
755 }
756 if (iNegStyle == 1 || iNegStyle == 3) {
757 if (CJS_Field* fTarget = pEventContext->TargetField()) {
758 v8::Local<v8::Array> arColor = pRuntime->NewArray();
759 pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString("RGB"));
760 pRuntime->PutArrayElement(arColor, 1, pRuntime->NewNumber(1));
761 pRuntime->PutArrayElement(arColor, 2, pRuntime->NewNumber(0));
762 pRuntime->PutArrayElement(arColor, 3, pRuntime->NewNumber(0));
763 fTarget->set_text_color(pRuntime, arColor);
764 }
765 }
766 } else {
767 if (iNegStyle == 1 || iNegStyle == 3) {
768 if (CJS_Field* fTarget = pEventContext->TargetField()) {
769 v8::Local<v8::Array> arColor = pRuntime->NewArray();
770 pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString("RGB"));
771 pRuntime->PutArrayElement(arColor, 1, pRuntime->NewNumber(0));
772 pRuntime->PutArrayElement(arColor, 2, pRuntime->NewNumber(0));
773 pRuntime->PutArrayElement(arColor, 3, pRuntime->NewNumber(0));
774
775 CJS_Result result = fTarget->get_text_color(pRuntime);
776 CFX_Color crProp = CJS_Color::ConvertArrayToPWLColor(
777 pRuntime, pRuntime->ToArray(result.Return()));
778 CFX_Color crColor =
779 CJS_Color::ConvertArrayToPWLColor(pRuntime, arColor);
780 if (crColor != crProp)
781 fTarget->set_text_color(pRuntime, arColor);
782 }
783 }
784 }
785 #endif
786 return CJS_Result::Success();
787 }
788
789 // function AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency,
790 // bCurrencyPrepend)
AFNumber_Keystroke(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)791 CJS_Result CJS_PublicMethods::AFNumber_Keystroke(
792 CJS_Runtime* pRuntime,
793 const std::vector<v8::Local<v8::Value>>& params) {
794 if (params.size() < 2)
795 return CJS_Result::Failure(JSMessage::kParamError);
796
797 CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
798 CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
799 if (!pEvent->HasValue())
800 return CJS_Result::Failure(JSMessage::kBadObjectError);
801
802 WideString& val = pEvent->Value();
803 WideString& wstrChange = pEvent->Change();
804 WideString wstrValue = val;
805
806 if (pEvent->WillCommit()) {
807 WideString swTemp = StrTrim(wstrValue);
808 if (swTemp.IsEmpty())
809 return CJS_Result::Success();
810
811 NormalizeDecimalMarkW(&swTemp);
812 if (!IsNumber(swTemp)) {
813 pEvent->Rc() = false;
814 WideString sError = JSGetStringFromID(JSMessage::kInvalidInputError);
815 AlertIfPossible(pContext, L"AFNumber_Keystroke", sError);
816 return CJS_Result::Failure(sError);
817 }
818 // It happens after the last keystroke and before validating,
819 return CJS_Result::Success();
820 }
821
822 WideString wstrSelected;
823 if (pEvent->SelStart() != -1) {
824 wstrSelected = wstrValue.Substr(pEvent->SelStart(),
825 pEvent->SelEnd() - pEvent->SelStart());
826 }
827
828 bool bHasSign = wstrValue.Contains(L'-') && !wstrSelected.Contains(L'-');
829 if (bHasSign) {
830 // can't insert "change" in front of sign position.
831 if (!wstrSelected.IsEmpty() && pEvent->SelStart() == 0) {
832 pEvent->Rc() = false;
833 return CJS_Result::Success();
834 }
835 }
836
837 int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1]));
838 const wchar_t cSep = DecimalMarkForStyle(iSepStyle);
839
840 bool bHasSep = wstrValue.Contains(cSep);
841 for (size_t i = 0; i < wstrChange.GetLength(); ++i) {
842 if (wstrChange[i] == cSep) {
843 if (bHasSep) {
844 pEvent->Rc() = false;
845 return CJS_Result::Success();
846 }
847 bHasSep = true;
848 continue;
849 }
850 if (wstrChange[i] == L'-') {
851 if (bHasSign) {
852 pEvent->Rc() = false;
853 return CJS_Result::Success();
854 }
855 // sign's position is not correct
856 if (i != 0) {
857 pEvent->Rc() = false;
858 return CJS_Result::Success();
859 }
860 if (pEvent->SelStart() != 0) {
861 pEvent->Rc() = false;
862 return CJS_Result::Success();
863 }
864 bHasSign = true;
865 continue;
866 }
867
868 if (!FXSYS_IsDecimalDigit(wstrChange[i])) {
869 pEvent->Rc() = false;
870 return CJS_Result::Success();
871 }
872 }
873
874 val = CalcMergedString(pEvent, wstrValue, wstrChange);
875 return CJS_Result::Success();
876 }
877
878 // function AFPercent_Format(nDec, sepStyle, bPercentPrepend)
AFPercent_Format(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)879 CJS_Result CJS_PublicMethods::AFPercent_Format(
880 CJS_Runtime* pRuntime,
881 const std::vector<v8::Local<v8::Value>>& params) {
882 #if !defined(OS_ANDROID)
883 if (params.size() < 2)
884 return CJS_Result::Failure(JSMessage::kParamError);
885
886 CJS_EventRecorder* pEvent =
887 pRuntime->GetCurrentEventContext()->GetEventRecorder();
888 if (!pEvent->HasValue())
889 return CJS_Result::Failure(JSMessage::kBadObjectError);
890
891 // Acrobat will accept this. Anything larger causes it to throw an error.
892 static constexpr int kMaxSepStyle = 49;
893
894 int iDec = pRuntime->ToInt32(params[0]);
895 int iSepStyle = pRuntime->ToInt32(params[1]);
896 // TODO(thestig): How do we handle negative raw |bPercentPrepend| values?
897 bool bPercentPrepend = params.size() > 2 && pRuntime->ToBoolean(params[2]);
898 if (iDec < 0 || iSepStyle < 0 || iSepStyle > kMaxSepStyle)
899 return CJS_Result::Failure(JSMessage::kValueError);
900
901 // When the |iDec| value is too big, Acrobat will just return "%".
902 static constexpr int kDecLimit = 512;
903 // This count must be in sync with |kDecLimit|.
904 static constexpr size_t kDigitsInDecLimit = 3;
905 WideString& Value = pEvent->Value();
906 if (iDec > kDecLimit) {
907 Value = L"%";
908 return CJS_Result::Success();
909 }
910
911 ByteString strValue = StrTrim(Value.ToDefANSI());
912 if (strValue.IsEmpty())
913 strValue = "0";
914
915 // for processing decimal places
916 double dValue = atof(strValue.c_str());
917 dValue *= 100;
918
919 size_t szNewSize;
920 {
921 // Figure out the format to use with FXSYS_snprintf() below.
922 // |format| is small because |iDec| is limited in size.
923 char format[sizeof("%.f") + kDigitsInDecLimit]; // e.g. "%.512f"
924 FXSYS_snprintf(format, sizeof(format), "%%.%df", iDec);
925
926 // Calculate the new size for |strValue| and get a span.
927 size_t szBufferSize = iDec + 3; // Negative sign, decimal point, and NUL.
928 double dValueCopy = fabs(dValue);
929 while (dValueCopy > 1) {
930 dValueCopy /= 10;
931 ++szBufferSize;
932 }
933
934 // Write into |strValue|.
935 pdfium::span<char> span = strValue.GetBuffer(szBufferSize);
936 FXSYS_snprintf(span.data(), szBufferSize, format, dValue);
937 szNewSize = strlen(span.data());
938 }
939 strValue.ReleaseBuffer(szNewSize);
940
941 // for processing separator style
942 Optional<size_t> mark_pos = strValue.Find('.');
943 if (mark_pos.has_value()) {
944 char mark = DecimalMarkForStyle(iSepStyle);
945 if (mark != '.')
946 strValue.SetAt(mark_pos.value(), mark);
947 }
948 bool bUseDigitSeparator = IsStyleWithDigitSeparator(iSepStyle);
949 if (bUseDigitSeparator || IsStyleWithApostropheSeparator(iSepStyle)) {
950 char cSeparator =
951 bUseDigitSeparator ? DigitSeparatorForStyle(iSepStyle) : '\'';
952 int iEnd = mark_pos.value_or(strValue.GetLength());
953 int iStop = dValue < 0 ? 1 : 0;
954 for (int i = iEnd - 3; i > iStop; i -= 3)
955 strValue.Insert(i, cSeparator);
956 }
957
958 if (bPercentPrepend)
959 strValue.InsertAtFront('%');
960 else
961 strValue.InsertAtBack('%');
962 Value = WideString::FromDefANSI(strValue.AsStringView());
963 #endif
964 return CJS_Result::Success();
965 }
966
967 // AFPercent_Keystroke(nDec, sepStyle)
AFPercent_Keystroke(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)968 CJS_Result CJS_PublicMethods::AFPercent_Keystroke(
969 CJS_Runtime* pRuntime,
970 const std::vector<v8::Local<v8::Value>>& params) {
971 return AFNumber_Keystroke(pRuntime, params);
972 }
973
974 // function AFDate_FormatEx(cFormat)
AFDate_FormatEx(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)975 CJS_Result CJS_PublicMethods::AFDate_FormatEx(
976 CJS_Runtime* pRuntime,
977 const std::vector<v8::Local<v8::Value>>& params) {
978 if (params.size() != 1)
979 return CJS_Result::Failure(JSMessage::kParamError);
980
981 CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
982 CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
983 if (!pEvent->HasValue())
984 return CJS_Result::Failure(JSMessage::kBadObjectError);
985
986 WideString& val = pEvent->Value();
987 WideString strValue = val;
988 if (strValue.IsEmpty())
989 return CJS_Result::Success();
990
991 WideString sFormat = pRuntime->ToWideString(params[0]);
992 double dDate;
993 if (strValue.Contains(L"GMT")) {
994 // e.g. "Tue Aug 11 14:24:16 GMT+08002009"
995 dDate = ParseDateAsGMT(strValue);
996 } else {
997 dDate = ParseDateUsingFormat(strValue, sFormat, nullptr);
998 }
999
1000 if (std::isnan(dDate)) {
1001 WideString swMsg = WideString::Format(
1002 JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
1003 AlertIfPossible(pContext, L"AFDate_FormatEx", swMsg);
1004 return CJS_Result::Failure(JSMessage::kParseDateError);
1005 }
1006
1007 val = PrintDateUsingFormat(dDate, sFormat);
1008 return CJS_Result::Success();
1009 }
1010
ParseDateAsGMT(const WideString & strValue)1011 double CJS_PublicMethods::ParseDateAsGMT(const WideString& strValue) {
1012 std::vector<WideString> wsArray;
1013 WideString sTemp;
1014 for (const auto& c : strValue) {
1015 if (c == L' ' || c == L':') {
1016 wsArray.push_back(std::move(sTemp));
1017 continue;
1018 }
1019 sTemp += c;
1020 }
1021 wsArray.push_back(std::move(sTemp));
1022 if (wsArray.size() != 8)
1023 return 0;
1024
1025 int nMonth = 1;
1026 sTemp = wsArray[1];
1027 for (size_t i = 0; i < pdfium::size(fxjs::kMonths); ++i) {
1028 if (sTemp.Compare(fxjs::kMonths[i]) == 0) {
1029 nMonth = i + 1;
1030 break;
1031 }
1032 }
1033
1034 int nDay = StringToFloat(wsArray[2].AsStringView());
1035 int nHour = StringToFloat(wsArray[3].AsStringView());
1036 int nMin = StringToFloat(wsArray[4].AsStringView());
1037 int nSec = StringToFloat(wsArray[5].AsStringView());
1038 int nYear = StringToFloat(wsArray[7].AsStringView());
1039 double dRet = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
1040 FX_MakeTime(nHour, nMin, nSec, 0));
1041 if (std::isnan(dRet))
1042 dRet = JS_DateParse(strValue);
1043
1044 return dRet;
1045 }
1046
1047 // AFDate_KeystrokeEx(cFormat)
AFDate_KeystrokeEx(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1048 CJS_Result CJS_PublicMethods::AFDate_KeystrokeEx(
1049 CJS_Runtime* pRuntime,
1050 const std::vector<v8::Local<v8::Value>>& params) {
1051 if (params.size() != 1) {
1052 return CJS_Result::Failure(WideString::FromASCII(
1053 "AFDate_KeystrokeEx's parameter size not correct"));
1054 }
1055
1056 CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
1057 CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
1058 if (!pEvent->WillCommit())
1059 return CJS_Result::Success();
1060
1061 if (!pEvent->HasValue())
1062 return CJS_Result::Failure(JSMessage::kBadObjectError);
1063
1064 const WideString& strValue = pEvent->Value();
1065 if (strValue.IsEmpty())
1066 return CJS_Result::Success();
1067
1068 bool bWrongFormat = false;
1069 WideString sFormat = pRuntime->ToWideString(params[0]);
1070 double dRet = ParseDateUsingFormat(strValue, sFormat, &bWrongFormat);
1071 if (bWrongFormat || std::isnan(dRet)) {
1072 WideString swMsg = WideString::Format(
1073 JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
1074 AlertIfPossible(pContext, L"AFDate_KeystrokeEx", swMsg);
1075 pEvent->Rc() = false;
1076 }
1077 return CJS_Result::Success();
1078 }
1079
AFDate_Format(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1080 CJS_Result CJS_PublicMethods::AFDate_Format(
1081 CJS_Runtime* pRuntime,
1082 const std::vector<v8::Local<v8::Value>>& params) {
1083 if (params.size() != 1)
1084 return CJS_Result::Failure(JSMessage::kParamError);
1085
1086 int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
1087 pdfium::size(kDateFormats));
1088 std::vector<v8::Local<v8::Value>> newParams;
1089 newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
1090 return AFDate_FormatEx(pRuntime, newParams);
1091 }
1092
1093 // AFDate_KeystrokeEx(cFormat)
AFDate_Keystroke(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1094 CJS_Result CJS_PublicMethods::AFDate_Keystroke(
1095 CJS_Runtime* pRuntime,
1096 const std::vector<v8::Local<v8::Value>>& params) {
1097 if (params.size() != 1)
1098 return CJS_Result::Failure(JSMessage::kParamError);
1099
1100 int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
1101 pdfium::size(kDateFormats));
1102 std::vector<v8::Local<v8::Value>> newParams;
1103 newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
1104 return AFDate_KeystrokeEx(pRuntime, newParams);
1105 }
1106
1107 // function AFTime_Format(ptf)
AFTime_Format(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1108 CJS_Result CJS_PublicMethods::AFTime_Format(
1109 CJS_Runtime* pRuntime,
1110 const std::vector<v8::Local<v8::Value>>& params) {
1111 if (params.size() != 1)
1112 return CJS_Result::Failure(JSMessage::kParamError);
1113
1114 int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
1115 pdfium::size(kTimeFormats));
1116 std::vector<v8::Local<v8::Value>> newParams;
1117 newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
1118 return AFDate_FormatEx(pRuntime, newParams);
1119 }
1120
AFTime_Keystroke(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1121 CJS_Result CJS_PublicMethods::AFTime_Keystroke(
1122 CJS_Runtime* pRuntime,
1123 const std::vector<v8::Local<v8::Value>>& params) {
1124 if (params.size() != 1)
1125 return CJS_Result::Failure(JSMessage::kParamError);
1126
1127 int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
1128 pdfium::size(kTimeFormats));
1129 std::vector<v8::Local<v8::Value>> newParams;
1130 newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
1131 return AFDate_KeystrokeEx(pRuntime, newParams);
1132 }
1133
AFTime_FormatEx(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1134 CJS_Result CJS_PublicMethods::AFTime_FormatEx(
1135 CJS_Runtime* pRuntime,
1136 const std::vector<v8::Local<v8::Value>>& params) {
1137 return AFDate_FormatEx(pRuntime, params);
1138 }
1139
AFTime_KeystrokeEx(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1140 CJS_Result CJS_PublicMethods::AFTime_KeystrokeEx(
1141 CJS_Runtime* pRuntime,
1142 const std::vector<v8::Local<v8::Value>>& params) {
1143 return AFDate_KeystrokeEx(pRuntime, params);
1144 }
1145
1146 // function AFSpecial_Format(psf)
AFSpecial_Format(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1147 CJS_Result CJS_PublicMethods::AFSpecial_Format(
1148 CJS_Runtime* pRuntime,
1149 const std::vector<v8::Local<v8::Value>>& params) {
1150 if (params.size() != 1)
1151 return CJS_Result::Failure(JSMessage::kParamError);
1152
1153 CJS_EventRecorder* pEvent =
1154 pRuntime->GetCurrentEventContext()->GetEventRecorder();
1155 if (!pEvent->HasValue())
1156 return CJS_Result::Failure(JSMessage::kBadObjectError);
1157
1158 const WideString& wsSource = pEvent->Value();
1159 WideString wsFormat;
1160 switch (pRuntime->ToInt32(params[0])) {
1161 case 0:
1162 wsFormat = L"99999";
1163 break;
1164 case 1:
1165 wsFormat = L"99999-9999";
1166 break;
1167 case 2:
1168 if (CJS_Util::StringPrintx(L"9999999999", wsSource).GetLength() >= 10)
1169 wsFormat = L"(999) 999-9999";
1170 else
1171 wsFormat = L"999-9999";
1172 break;
1173 case 3:
1174 wsFormat = L"999-99-9999";
1175 break;
1176 }
1177
1178 pEvent->Value() = CJS_Util::StringPrintx(wsFormat, wsSource);
1179 return CJS_Result::Success();
1180 }
1181
1182 // function AFSpecial_KeystrokeEx(mask)
AFSpecial_KeystrokeEx(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1183 CJS_Result CJS_PublicMethods::AFSpecial_KeystrokeEx(
1184 CJS_Runtime* pRuntime,
1185 const std::vector<v8::Local<v8::Value>>& params) {
1186 if (params.size() < 1)
1187 return CJS_Result::Failure(JSMessage::kParamError);
1188
1189 CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
1190 CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
1191 if (!pEvent->HasValue())
1192 return CJS_Result::Failure(JSMessage::kBadObjectError);
1193
1194 const WideString& valEvent = pEvent->Value();
1195 WideString wstrMask = pRuntime->ToWideString(params[0]);
1196 if (wstrMask.IsEmpty())
1197 return CJS_Result::Success();
1198
1199 if (pEvent->WillCommit()) {
1200 if (valEvent.IsEmpty())
1201 return CJS_Result::Success();
1202
1203 if (valEvent.GetLength() > wstrMask.GetLength()) {
1204 AlertIfPossible(pContext, L"AFSpecial_KeystrokeEx",
1205 JSGetStringFromID(JSMessage::kParamTooLongError));
1206 pEvent->Rc() = false;
1207 return CJS_Result::Success();
1208 }
1209
1210 size_t iIndex = 0;
1211 for (iIndex = 0; iIndex < valEvent.GetLength(); ++iIndex) {
1212 if (!MaskSatisfied(valEvent[iIndex], wstrMask[iIndex]))
1213 break;
1214 }
1215 if (iIndex != wstrMask.GetLength()) {
1216 AlertIfPossible(pContext, L"AFSpecial_KeystrokeEx",
1217 JSGetStringFromID(JSMessage::kInvalidInputError));
1218 pEvent->Rc() = false;
1219 }
1220 return CJS_Result::Success();
1221 }
1222
1223 WideString& wideChange = pEvent->Change();
1224 if (wideChange.IsEmpty())
1225 return CJS_Result::Success();
1226
1227 WideString wChange = wideChange;
1228 size_t iIndexMask = pEvent->SelStart();
1229 size_t combined_len = valEvent.GetLength() + wChange.GetLength() +
1230 pEvent->SelStart() - pEvent->SelEnd();
1231 if (combined_len > wstrMask.GetLength()) {
1232 AlertIfPossible(pContext, L"AFSpecial_KeystrokeEx",
1233 JSGetStringFromID(JSMessage::kParamTooLongError));
1234 pEvent->Rc() = false;
1235 return CJS_Result::Success();
1236 }
1237
1238 if (iIndexMask >= wstrMask.GetLength() && !wChange.IsEmpty()) {
1239 AlertIfPossible(pContext, L"AFSpecial_KeystrokeEx",
1240 JSGetStringFromID(JSMessage::kParamTooLongError));
1241 pEvent->Rc() = false;
1242 return CJS_Result::Success();
1243 }
1244
1245 for (size_t i = 0; i < wChange.GetLength(); ++i) {
1246 if (iIndexMask >= wstrMask.GetLength()) {
1247 AlertIfPossible(pContext, L"AFSpecial_KeystrokeEx",
1248 JSGetStringFromID(JSMessage::kParamTooLongError));
1249 pEvent->Rc() = false;
1250 return CJS_Result::Success();
1251 }
1252 wchar_t wMask = wstrMask[iIndexMask];
1253 if (!IsReservedMaskChar(wMask))
1254 wChange.SetAt(i, wMask);
1255
1256 if (!MaskSatisfied(wChange[i], wMask)) {
1257 pEvent->Rc() = false;
1258 return CJS_Result::Success();
1259 }
1260 iIndexMask++;
1261 }
1262 wideChange = std::move(wChange);
1263 return CJS_Result::Success();
1264 }
1265
1266 // function AFSpecial_Keystroke(psf)
AFSpecial_Keystroke(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1267 CJS_Result CJS_PublicMethods::AFSpecial_Keystroke(
1268 CJS_Runtime* pRuntime,
1269 const std::vector<v8::Local<v8::Value>>& params) {
1270 if (params.size() != 1)
1271 return CJS_Result::Failure(JSMessage::kParamError);
1272
1273 CJS_EventRecorder* pEvent =
1274 pRuntime->GetCurrentEventContext()->GetEventRecorder();
1275 if (!pEvent->HasValue())
1276 return CJS_Result::Failure(JSMessage::kBadObjectError);
1277
1278 const char* cFormat = "";
1279 switch (pRuntime->ToInt32(params[0])) {
1280 case 0:
1281 cFormat = "99999";
1282 break;
1283 case 1:
1284 cFormat = "999999999";
1285 break;
1286 case 2:
1287 if (pEvent->Value().GetLength() + pEvent->Change().GetLength() > 7)
1288 cFormat = "9999999999";
1289 else
1290 cFormat = "9999999";
1291 break;
1292 case 3:
1293 cFormat = "999999999";
1294 break;
1295 }
1296
1297 std::vector<v8::Local<v8::Value>> params2;
1298 params2.push_back(pRuntime->NewString(cFormat));
1299 return AFSpecial_KeystrokeEx(pRuntime, params2);
1300 }
1301
AFMergeChange(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1302 CJS_Result CJS_PublicMethods::AFMergeChange(
1303 CJS_Runtime* pRuntime,
1304 const std::vector<v8::Local<v8::Value>>& params) {
1305 if (params.size() != 1)
1306 return CJS_Result::Failure(JSMessage::kParamError);
1307
1308 CJS_EventRecorder* pEventRecorder =
1309 pRuntime->GetCurrentEventContext()->GetEventRecorder();
1310
1311 WideString swValue;
1312 if (pEventRecorder->HasValue())
1313 swValue = pEventRecorder->Value();
1314
1315 if (pEventRecorder->WillCommit())
1316 return CJS_Result::Success(pRuntime->NewString(swValue.AsStringView()));
1317
1318 return CJS_Result::Success(pRuntime->NewString(
1319 CalcMergedString(pEventRecorder, swValue, pEventRecorder->Change())
1320 .AsStringView()));
1321 }
1322
AFParseDateEx(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1323 CJS_Result CJS_PublicMethods::AFParseDateEx(
1324 CJS_Runtime* pRuntime,
1325 const std::vector<v8::Local<v8::Value>>& params) {
1326 if (params.size() != 2)
1327 return CJS_Result::Failure(JSMessage::kParamError);
1328
1329 WideString sValue = pRuntime->ToWideString(params[0]);
1330 WideString sFormat = pRuntime->ToWideString(params[1]);
1331 double dDate = ParseDateUsingFormat(sValue, sFormat, nullptr);
1332 if (std::isnan(dDate)) {
1333 WideString swMsg = WideString::Format(
1334 JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
1335 AlertIfPossible(pRuntime->GetCurrentEventContext(), L"AFParseDateEx",
1336 swMsg);
1337 return CJS_Result::Failure(JSMessage::kParseDateError);
1338 }
1339 return CJS_Result::Success(pRuntime->NewNumber(dDate));
1340 }
1341
AFSimple(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1342 CJS_Result CJS_PublicMethods::AFSimple(
1343 CJS_Runtime* pRuntime,
1344 const std::vector<v8::Local<v8::Value>>& params) {
1345 if (params.size() != 3)
1346 return CJS_Result::Failure(JSMessage::kParamError);
1347
1348 WideString sFunction = pRuntime->ToWideString(params[0]);
1349 double arg1 = pRuntime->ToDouble(params[1]);
1350 double arg2 = pRuntime->ToDouble(params[2]);
1351 if (std::isnan(arg1) || std::isnan(arg2))
1352 return CJS_Result::Failure(JSMessage::kValueError);
1353
1354 Optional<double> result = ApplyNamedOperation(sFunction.c_str(), arg1, arg2);
1355 if (!result.has_value())
1356 return CJS_Result::Failure(JSMessage::kValueError);
1357
1358 double dValue = result.value();
1359 if (wcscmp(sFunction.c_str(), L"AVG") == 0)
1360 dValue /= 2.0;
1361
1362 return CJS_Result::Success(pRuntime->NewNumber(dValue));
1363 }
1364
AFMakeNumber(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1365 CJS_Result CJS_PublicMethods::AFMakeNumber(
1366 CJS_Runtime* pRuntime,
1367 const std::vector<v8::Local<v8::Value>>& params) {
1368 if (params.size() != 1)
1369 return CJS_Result::Failure(JSMessage::kParamError);
1370
1371 WideString ws = pRuntime->ToWideString(params[0]);
1372 NormalizeDecimalMarkW(&ws);
1373
1374 v8::Local<v8::Value> val =
1375 pRuntime->MaybeCoerceToNumber(pRuntime->NewString(ws.AsStringView()));
1376 if (!val->IsNumber())
1377 return CJS_Result::Success(pRuntime->NewNumber(0));
1378
1379 return CJS_Result::Success(val);
1380 }
1381
AFSimple_Calculate(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1382 CJS_Result CJS_PublicMethods::AFSimple_Calculate(
1383 CJS_Runtime* pRuntime,
1384 const std::vector<v8::Local<v8::Value>>& params) {
1385 if (params.size() != 2)
1386 return CJS_Result::Failure(JSMessage::kParamError);
1387
1388 if (params[1].IsEmpty() || (!params[1]->IsArray() && !params[1]->IsString()))
1389 return CJS_Result::Failure(JSMessage::kParamError);
1390
1391 WideString sFunction = pRuntime->ToWideString(params[0]);
1392 v8::Local<v8::Array> FieldNameArray =
1393 AF_MakeArrayFromList(pRuntime, params[1]);
1394
1395 CPDFSDK_InteractiveForm* pReaderForm =
1396 pRuntime->GetFormFillEnv()->GetInteractiveForm();
1397 CPDF_InteractiveForm* pForm = pReaderForm->GetInteractiveForm();
1398
1399 double dValue = wcscmp(sFunction.c_str(), L"PRD") == 0 ? 1.0 : 0.0;
1400 int nFieldsCount = 0;
1401 for (size_t i = 0; i < pRuntime->GetArrayLength(FieldNameArray); ++i) {
1402 WideString wsFieldName =
1403 pRuntime->ToWideString(pRuntime->GetArrayElement(FieldNameArray, i));
1404
1405 for (size_t j = 0; j < pForm->CountFields(wsFieldName); ++j) {
1406 CPDF_FormField* pFormField = pForm->GetField(j, wsFieldName);
1407 if (!pFormField)
1408 continue;
1409
1410 double dTemp = 0.0;
1411 switch (pFormField->GetFieldType()) {
1412 case FormFieldType::kTextField:
1413 case FormFieldType::kComboBox: {
1414 WideString trimmed = pFormField->GetValue();
1415 trimmed.TrimRight();
1416 trimmed.TrimLeft();
1417 dTemp = StringToDouble(trimmed.AsStringView());
1418 break;
1419 }
1420 case FormFieldType::kPushButton:
1421 break;
1422 case FormFieldType::kCheckBox:
1423 case FormFieldType::kRadioButton:
1424 for (int c = 0; c < pFormField->CountControls(); ++c) {
1425 CPDF_FormControl* pFormCtrl = pFormField->GetControl(c);
1426 if (!pFormField || !pFormCtrl->IsChecked())
1427 continue;
1428
1429 WideString trimmed = pFormCtrl->GetExportValue();
1430 trimmed.TrimRight();
1431 trimmed.TrimLeft();
1432 dTemp = StringToFloat(trimmed.AsStringView());
1433 break;
1434 }
1435 break;
1436 case FormFieldType::kListBox:
1437 if (pFormField->CountSelectedItems() <= 1) {
1438 WideString trimmed = pFormField->GetValue();
1439 trimmed.TrimRight();
1440 trimmed.TrimLeft();
1441 dTemp = StringToFloat(trimmed.AsStringView());
1442 }
1443 break;
1444 default:
1445 break;
1446 }
1447
1448 if (i == 0 && j == 0 &&
1449 (wcscmp(sFunction.c_str(), L"MIN") == 0 ||
1450 wcscmp(sFunction.c_str(), L"MAX") == 0)) {
1451 dValue = dTemp;
1452 }
1453 Optional<double> dResult =
1454 ApplyNamedOperation(sFunction.c_str(), dValue, dTemp);
1455 if (!dResult.has_value())
1456 return CJS_Result::Failure(JSMessage::kValueError);
1457
1458 dValue = dResult.value();
1459 nFieldsCount++;
1460 }
1461 }
1462
1463 if (wcscmp(sFunction.c_str(), L"AVG") == 0 && nFieldsCount > 0)
1464 dValue /= nFieldsCount;
1465
1466 dValue = floor(dValue * FXSYS_pow(10, 6) + 0.49) / FXSYS_pow(10, 6);
1467
1468 CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
1469 if (pContext->GetEventRecorder()->HasValue()) {
1470 pContext->GetEventRecorder()->Value() =
1471 pRuntime->ToWideString(pRuntime->NewNumber(dValue));
1472 }
1473
1474 return CJS_Result::Success();
1475 }
1476
1477 // This function validates the current event to ensure that its value is
1478 // within the specified range.
AFRange_Validate(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1479 CJS_Result CJS_PublicMethods::AFRange_Validate(
1480 CJS_Runtime* pRuntime,
1481 const std::vector<v8::Local<v8::Value>>& params) {
1482 if (params.size() != 4)
1483 return CJS_Result::Failure(JSMessage::kParamError);
1484
1485 CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
1486 CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
1487 if (!pEvent->HasValue())
1488 return CJS_Result::Failure(JSMessage::kBadObjectError);
1489
1490 if (pEvent->Value().IsEmpty())
1491 return CJS_Result::Success();
1492
1493 double dEentValue = atof(pEvent->Value().ToDefANSI().c_str());
1494 bool bGreaterThan = pRuntime->ToBoolean(params[0]);
1495 double dGreaterThan = pRuntime->ToDouble(params[1]);
1496 bool bLessThan = pRuntime->ToBoolean(params[2]);
1497 double dLessThan = pRuntime->ToDouble(params[3]);
1498 WideString swMsg;
1499
1500 if (bGreaterThan && bLessThan) {
1501 if (dEentValue < dGreaterThan || dEentValue > dLessThan)
1502 swMsg = WideString::Format(
1503 JSGetStringFromID(JSMessage::kRangeBetweenError).c_str(),
1504 pRuntime->ToWideString(params[1]).c_str(),
1505 pRuntime->ToWideString(params[3]).c_str());
1506 } else if (bGreaterThan) {
1507 if (dEentValue < dGreaterThan)
1508 swMsg = WideString::Format(
1509 JSGetStringFromID(JSMessage::kRangeGreaterError).c_str(),
1510 pRuntime->ToWideString(params[1]).c_str());
1511 } else if (bLessThan) {
1512 if (dEentValue > dLessThan)
1513 swMsg = WideString::Format(
1514 JSGetStringFromID(JSMessage::kRangeLessError).c_str(),
1515 pRuntime->ToWideString(params[3]).c_str());
1516 }
1517
1518 if (!swMsg.IsEmpty()) {
1519 AlertIfPossible(pContext, L"AFRange_Validate", swMsg);
1520 pEvent->Rc() = false;
1521 }
1522 return CJS_Result::Success();
1523 }
1524
AFExtractNums(CJS_Runtime * pRuntime,const std::vector<v8::Local<v8::Value>> & params)1525 CJS_Result CJS_PublicMethods::AFExtractNums(
1526 CJS_Runtime* pRuntime,
1527 const std::vector<v8::Local<v8::Value>>& params) {
1528 if (params.size() != 1)
1529 return CJS_Result::Failure(JSMessage::kParamError);
1530
1531 WideString str = pRuntime->ToWideString(params[0]);
1532 if (str.GetLength() > 0 && IsDigitSeparatorOrDecimalMark(str[0]))
1533 str.InsertAtFront(L'0');
1534
1535 WideString sPart;
1536 v8::Local<v8::Array> nums = pRuntime->NewArray();
1537 int nIndex = 0;
1538 for (const auto& wc : str) {
1539 if (FXSYS_IsDecimalDigit(wc)) {
1540 sPart += wc;
1541 } else if (sPart.GetLength() > 0) {
1542 pRuntime->PutArrayElement(nums, nIndex,
1543 pRuntime->NewString(sPart.AsStringView()));
1544 sPart.clear();
1545 nIndex++;
1546 }
1547 }
1548 if (sPart.GetLength() > 0) {
1549 pRuntime->PutArrayElement(nums, nIndex,
1550 pRuntime->NewString(sPart.AsStringView()));
1551 }
1552 if (pRuntime->GetArrayLength(nums) > 0)
1553 return CJS_Result::Success(nums);
1554
1555 return CJS_Result::Success(pRuntime->NewUndefined());
1556 }
1557