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