Home | History | Annotate | Download | only in fxjs
      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>
     74 T StrTrim(const T& str) {
     75   T result = str;
     76   result.Trim(' ');
     77   return result;
     78 }
     79 
     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_
     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 
    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>>&)>
    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 
    142 int WithinBoundsOrZero(int value, size_t size) {
    143   return value >= 0 && static_cast<size_t>(value) < size ? value : 0;
    144 }
    145 
    146 int ValidStyleOrZero(int style) {
    147   return WithinBoundsOrZero(style, 4);
    148 }
    149 
    150 bool IsDigitSeparatorOrDecimalMark(int c) {
    151   return c == '.' || c == ',';
    152 }
    153 
    154 #if _FX_OS_ != _FX_OS_ANDROID_
    155 bool IsStyleWithDigitSeparator(int style) {
    156   return style == 0 || style == 2;
    157 }
    158 
    159 char DigitSeparatorForStyle(int style) {
    160   ASSERT(IsStyleWithDigitSeparator(style));
    161   return style == 0 ? ',' : '.';
    162 }
    163 #endif
    164 
    165 bool IsStyleWithCommaDecimalMark(int style) {
    166   return style >= 2;
    167 }
    168 
    169 char DecimalMarkForStyle(int style) {
    170   return IsStyleWithCommaDecimalMark(style) ? ',' : '.';
    171 }
    172 
    173 #if _FX_OS_ != _FX_OS_ANDROID_
    174 void NormalizeDecimalMark(ByteString* str) {
    175   str->Replace(",", ".");
    176 }
    177 #endif
    178 
    179 void NormalizeDecimalMarkW(WideString* str) {
    180   str->Replace(L",", L".");
    181 }
    182 
    183 bool IsValidMonth(int m) {
    184   return m >= 1 && m <= 12;
    185 }
    186 
    187 // TODO(thestig): Should this take the month into consideration?
    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.
    193 bool IsValid24Hour(int h) {
    194   return h >= 0 && h <= 24;
    195 }
    196 
    197 bool IsValidMinute(int m) {
    198   return m >= 0 && m <= 60;
    199 }
    200 
    201 bool IsValidSecond(int s) {
    202   return s >= 0 && s <= 60;
    203 }
    204 
    205 }  // namespace
    206 
    207 CJS_PublicMethods::CJS_PublicMethods(v8::Local<v8::Object> pObject)
    208     : CJS_Object(pObject) {}
    209 
    210 CJS_PublicMethods::~CJS_PublicMethods() {}
    211 
    212 // static
    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 
    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 
    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 
    296 bool CJS_PublicMethods::IsReservedMaskChar(wchar_t ch) {
    297   return ch == L'9' || ch == L'A' || ch == L'O' || ch == L'X';
    298 }
    299 
    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 
    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 
    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 
    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 
    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 
    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 
    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)
    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)
    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)
   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)
   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)
   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 
   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)
   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 
   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)
   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)
   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 
   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 
   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 
   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)
   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)
   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)
   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 
   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 
   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 
   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 
   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 
   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. */
   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 
   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