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