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