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