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