blob: e868fcce84a532cc5cc99f1d6dfcccf53494383b [file] [log] [blame]
K. Moon832a6942022-10-31 20:11:31 +00001// Copyright 2014 The PDFium Authors
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Lei Zhanga6d9f0e2015-06-13 00:48:38 -07004
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07005// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
Dan Sinclaire0345a42017-10-30 20:20:42 +00007#include "fxjs/cjs_util.h"
Tom Sepez37458412015-10-06 11:33:46 -07008
Lei Zhang15fef792021-08-04 17:52:39 +00009#include <math.h>
Lei Zhang6b37a5f2015-12-25 00:20:59 -080010#include <time.h>
11
tsepez86a61dc2016-03-25 10:00:11 -070012#include <algorithm>
Lei Zhang59a1a912021-06-07 16:55:25 +000013#include <string>
Dan Sinclair3ebd1212016-03-09 09:59:23 -050014#include <vector>
15
Lei Zhangdd77c862019-03-29 18:57:15 +000016#include "build/build_config.h"
Dan Sinclaircfb19442017-04-20 13:13:04 -040017#include "core/fxcrt/fx_extension.h"
Dan Sinclaire0345a42017-10-30 20:20:42 +000018#include "fxjs/cjs_event_context.h"
Dan Sinclaire0345a42017-10-30 20:20:42 +000019#include "fxjs/cjs_object.h"
20#include "fxjs/cjs_publicmethods.h"
21#include "fxjs/cjs_runtime.h"
Tom Sepez86e5fbf2018-11-01 21:21:52 +000022#include "fxjs/fx_date_helpers.h"
Tom Sepezaf6371f2020-11-02 20:13:01 +000023#include "fxjs/fxv8.h"
Tom Sepez221f0b32018-06-04 22:11:27 +000024#include "fxjs/js_define.h"
Dan Sinclaire0345a42017-10-30 20:20:42 +000025#include "fxjs/js_resources.h"
Lei Zhangce725742020-10-02 02:02:44 +000026#include "third_party/base/check_op.h"
Tom Sepez90589d42023-11-01 18:24:30 +000027#include "third_party/base/containers/span.h"
Dan Elphick05673a32021-09-09 15:42:55 +000028#include "v8/include/v8-date.h"
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070029
Lei Zhangeb2da2a2022-01-12 19:28:33 +000030#if BUILDFLAG(IS_ANDROID)
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070031#include <ctype.h>
32#endif
33
tsepez86a61dc2016-03-25 10:00:11 -070034namespace {
35
36// Map PDF-style directives to equivalent wcsftime directives. Not
37// all have direct equivalents, though.
38struct TbConvert {
Dan Sinclair812e96c2017-03-13 16:43:37 -040039 const wchar_t* lpszJSMark;
40 const wchar_t* lpszCppMark;
tsepez86a61dc2016-03-25 10:00:11 -070041};
42
43// Map PDF-style directives lacking direct wcsftime directives to
44// the value with which they will be replaced.
45struct TbConvertAdditional {
Lei Zhang6b170cf2020-10-09 20:17:51 +000046 wchar_t js_mark;
47 int value;
tsepez86a61dc2016-03-25 10:00:11 -070048};
49
50const TbConvert TbConvertTable[] = {
51 {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"},
52 {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"},
53 {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"},
54 {L"TT", L"%p"},
Lei Zhangf997abe2022-01-12 19:14:23 +000055#if BUILDFLAG(IS_WIN)
tsepez86a61dc2016-03-25 10:00:11 -070056 {L"tt", L"%p"}, {L"h", L"%#I"},
57#else
58 {L"tt", L"%P"}, {L"h", L"%l"},
59#endif
60};
61
Tom Sepez9b86cb62018-11-01 19:11:15 +000062enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
63
64wchar_t TranslateCase(wchar_t input, CaseMode eMode) {
65 if (eMode == kLowerCase && FXSYS_iswupper(input))
66 return input | 0x20;
67 if (eMode == kUpperCase && FXSYS_iswlower(input))
68 return input & ~0x20;
69 return input;
70}
71
tsepez86a61dc2016-03-25 10:00:11 -070072} // namespace
73
Dan Sinclairc94a7932017-10-26 16:48:57 -040074const JSMethodSpec CJS_Util::MethodSpecs[] = {
Dan Sinclair909fa2d2017-12-12 01:53:28 +000075 {"printd", printd_static},
76 {"printf", printf_static},
77 {"printx", printx_static},
78 {"scand", scand_static},
79 {"byteToChar", byteToChar_static}};
Dan Sinclairef299532017-10-26 16:48:30 -040080
Tom Sepezb4958712020-10-13 20:30:43 +000081uint32_t CJS_Util::ObjDefnID = 0;
Dan Sinclairf7435522018-02-05 22:27:22 +000082const char CJS_Util::kName[] = "util";
Dan Sinclairef299532017-10-26 16:48:30 -040083
84// static
Tom Sepezb4958712020-10-13 20:30:43 +000085uint32_t CJS_Util::GetObjDefnID() {
Lei Zhangad1f7b42018-07-11 13:04:43 +000086 return ObjDefnID;
87}
88
89// static
Dan Sinclairbef4d3e2017-10-26 16:49:38 -040090void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine) {
Dan Sinclairf7435522018-02-05 22:27:22 +000091 ObjDefnID = pEngine->DefineObj(CJS_Util::kName, FXJSOBJTYPE_STATIC,
Dan Sinclair998fee32018-02-05 21:43:19 +000092 JSConstructor<CJS_Util>, JSDestructor);
Tom Sepez8b4ddeb2018-06-11 15:55:17 +000093 DefineMethods(pEngine, ObjDefnID, MethodSpecs);
Dan Sinclairef299532017-10-26 16:48:30 -040094}
95
Tom Sepez36aae4f2018-06-04 19:44:37 +000096CJS_Util::CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
97 : CJS_Object(pObject, pRuntime) {}
Dan Sinclair998fee32018-02-05 21:43:19 +000098
Dan Sinclairf7435522018-02-05 22:27:22 +000099CJS_Util::~CJS_Util() = default;
tsepez86a61dc2016-03-25 10:00:11 -0700100
Tom Sepez3a6d0582018-08-17 19:28:52 +0000101CJS_Result CJS_Util::printf(CJS_Runtime* pRuntime,
Tom Sepez90589d42023-11-01 18:24:30 +0000102 pdfium::span<v8::Local<v8::Value>> params) {
Lei Zhang93fd8a82019-12-12 20:29:42 +0000103 const size_t num_params = params.size();
104 if (num_params < 1)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000105 return CJS_Result::Failure(JSMessage::kParamError);
Lei Zhang574b5742017-03-30 12:41:55 -0700106
Lei Zhang93fd8a82019-12-12 20:29:42 +0000107 // Use 'S' as a sentinel to ensure we always have some text before the first
108 // format specifier.
109 WideString unsafe_fmt_string = L'S' + pRuntime->ToWideString(params[0]);
110 std::vector<WideString> unsafe_conversion_specifiers;
111
112 {
113 size_t offset = 0;
114 while (true) {
Lei Zhang24c6be62024-02-08 20:06:48 +0000115 std::optional<size_t> offset_end =
Lei Zhang2c495302021-10-07 23:13:30 +0000116 unsafe_fmt_string.Find(L"%", offset + 1);
Lei Zhang93fd8a82019-12-12 20:29:42 +0000117 if (!offset_end.has_value()) {
118 unsafe_conversion_specifiers.push_back(
Daniel Hosseiniana9a704e2020-01-28 19:52:32 +0000119 unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset));
Lei Zhang93fd8a82019-12-12 20:29:42 +0000120 break;
121 }
122
123 unsafe_conversion_specifiers.push_back(
Daniel Hosseinian39516a52020-01-27 22:10:50 +0000124 unsafe_fmt_string.Substr(offset, offset_end.value() - offset));
Lei Zhang93fd8a82019-12-12 20:29:42 +0000125 offset = offset_end.value();
126 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700127 }
128
Lei Zhang93fd8a82019-12-12 20:29:42 +0000129 WideString result = unsafe_conversion_specifiers[0];
130 for (size_t i = 1; i < unsafe_conversion_specifiers.size(); ++i) {
131 WideString fmt = unsafe_conversion_specifiers[i];
132 if (i >= num_params) {
133 result += fmt;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700134 continue;
135 }
136
Lei Zhang93fd8a82019-12-12 20:29:42 +0000137 WideString segment;
138 switch (ParseDataType(&fmt)) {
Tom Sepez86390d72021-05-20 17:11:36 +0000139 case DataType::kInt:
Lei Zhang93fd8a82019-12-12 20:29:42 +0000140 segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i]));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700141 break;
Tom Sepez86390d72021-05-20 17:11:36 +0000142 case DataType::kDouble:
Lei Zhang93fd8a82019-12-12 20:29:42 +0000143 segment =
144 WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i]));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700145 break;
Tom Sepez86390d72021-05-20 17:11:36 +0000146 case DataType::kString:
Lei Zhang93fd8a82019-12-12 20:29:42 +0000147 segment = WideString::Format(fmt.c_str(),
148 pRuntime->ToWideString(params[i]).c_str());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700149 break;
150 default:
Lei Zhang93fd8a82019-12-12 20:29:42 +0000151 segment = WideString::Format(L"%ls", fmt.c_str());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700152 break;
153 }
Lei Zhang93fd8a82019-12-12 20:29:42 +0000154 result += segment;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700155 }
156
Lei Zhang93fd8a82019-12-12 20:29:42 +0000157 // Remove the 'S' sentinel introduced earlier.
158 DCHECK_EQ(L'S', result[0]);
159 auto result_view = result.AsStringView();
160 return CJS_Result::Success(
Daniel Hosseiniana9a704e2020-01-28 19:52:32 +0000161 pRuntime->NewString(result_view.Last(result_view.GetLength() - 1)));
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700162}
163
Tom Sepez3a6d0582018-08-17 19:28:52 +0000164CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime,
Tom Sepez90589d42023-11-01 18:24:30 +0000165 pdfium::span<v8::Local<v8::Value>> params) {
Lei Zhang574b5742017-03-30 12:41:55 -0700166 const size_t iSize = params.size();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700167 if (iSize < 2)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000168 return CJS_Result::Failure(JSMessage::kParamError);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700169
Tom Sepezaf6371f2020-11-02 20:13:01 +0000170 if (!fxv8::IsDate(params[1]))
Tom Sepez3a6d0582018-08-17 19:28:52 +0000171 return CJS_Result::Failure(JSMessage::kSecondParamNotDateError);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700172
Dan Sinclair6687f782017-10-25 14:34:26 -0400173 v8::Local<v8::Date> v8_date = params[1].As<v8::Date>();
Lei Zhang15fef792021-08-04 17:52:39 +0000174 if (v8_date.IsEmpty() || isnan(pRuntime->ToDouble(v8_date)))
Tom Sepez3a6d0582018-08-17 19:28:52 +0000175 return CJS_Result::Failure(JSMessage::kSecondParamInvalidDateError);
Dan Sinclair4c50b8b2017-10-25 15:36:11 -0400176
Tom Sepez86e5fbf2018-11-01 21:21:52 +0000177 double date = FX_LocalTime(pRuntime->ToDouble(v8_date));
178 int year = FX_GetYearFromTime(date);
179 int month = FX_GetMonthFromTime(date) + 1; // One-based.
180 int day = FX_GetDayFromTime(date);
181 int hour = FX_GetHourFromTime(date);
182 int min = FX_GetMinFromTime(date);
183 int sec = FX_GetSecFromTime(date);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700184
dan sinclair80435cb2017-10-24 21:40:24 -0400185 if (params[0]->IsNumber()) {
Ryan Harrison275e2602017-09-18 14:23:18 -0400186 WideString swResult;
dan sinclair80435cb2017-10-24 21:40:24 -0400187 switch (pRuntime->ToInt32(params[0])) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700188 case 0:
Dan Sinclair3f1c8322017-11-16 21:45:18 +0000189 swResult = WideString::Format(L"D:%04d%02d%02d%02d%02d%02d", year,
190 month, day, hour, min, sec);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700191 break;
192 case 1:
Dan Sinclair3f1c8322017-11-16 21:45:18 +0000193 swResult = WideString::Format(L"%04d.%02d.%02d %02d:%02d:%02d", year,
194 month, day, hour, min, sec);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700195 break;
196 case 2:
Dan Sinclair3f1c8322017-11-16 21:45:18 +0000197 swResult = WideString::Format(L"%04d/%02d/%02d %02d:%02d:%02d", year,
198 month, day, hour, min, sec);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700199 break;
200 default:
Tom Sepez3a6d0582018-08-17 19:28:52 +0000201 return CJS_Result::Failure(JSMessage::kValueError);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700202 }
203
Tom Sepez3a6d0582018-08-17 19:28:52 +0000204 return CJS_Result::Success(pRuntime->NewString(swResult.AsStringView()));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700205 }
tsepez86a61dc2016-03-25 10:00:11 -0700206
Tom Sepez20736f72018-08-17 16:44:50 +0000207 if (!params[0]->IsString())
Tom Sepez3a6d0582018-08-17 19:28:52 +0000208 return CJS_Result::Failure(JSMessage::kTypeError);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700209
Tom Sepez20736f72018-08-17 16:44:50 +0000210 // We don't support XFAPicture at the moment.
211 if (iSize > 2 && pRuntime->ToBoolean(params[2]))
Tom Sepez3a6d0582018-08-17 19:28:52 +0000212 return CJS_Result::Failure(JSMessage::kNotSupportedError);
tsepez86a61dc2016-03-25 10:00:11 -0700213
Tom Sepez20736f72018-08-17 16:44:50 +0000214 // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
215 // pre-existing %-directives before inserting our own.
Lei Zhang59a1a912021-06-07 16:55:25 +0000216 std::wstring cFormat = pRuntime->ToWideString(params[0]).c_str();
Tom Sepez20736f72018-08-17 16:44:50 +0000217 cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
218 cFormat.end());
219
Lei Zhang3111d402022-04-18 22:24:07 +0000220 for (size_t i = 0; i < std::size(TbConvertTable); ++i) {
Tom Sepeza0de6da2021-11-11 01:40:23 +0000221 size_t nFound = 0;
Anton Bikineev7ac13342022-01-24 21:25:15 +0000222 while (true) {
Tom Sepeza0de6da2021-11-11 01:40:23 +0000223 nFound = cFormat.find(TbConvertTable[i].lpszJSMark, nFound);
224 if (nFound == std::wstring::npos)
225 break;
226
227 cFormat.replace(nFound, wcslen(TbConvertTable[i].lpszJSMark),
Tom Sepez20736f72018-08-17 16:44:50 +0000228 TbConvertTable[i].lpszCppMark);
Tom Sepez20736f72018-08-17 16:44:50 +0000229 }
Lei Zhang002d9362018-12-08 00:04:41 +0000230 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700231
Lei Zhang002d9362018-12-08 00:04:41 +0000232 if (year < 0)
233 return CJS_Result::Failure(JSMessage::kValueError);
Lei Zhang2bf942d2017-06-16 13:48:19 -0700234
Lei Zhang002d9362018-12-08 00:04:41 +0000235 const TbConvertAdditional cTableAd[] = {
Lei Zhang6b170cf2020-10-09 20:17:51 +0000236 {L'm', month}, {L'd', day},
237 {L'H', hour}, {L'h', hour > 12 ? hour - 12 : hour},
238 {L'M', min}, {L's', sec},
Lei Zhang002d9362018-12-08 00:04:41 +0000239 };
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700240
Lei Zhang3111d402022-04-18 22:24:07 +0000241 for (size_t i = 0; i < std::size(cTableAd); ++i) {
Tom Sepeza0de6da2021-11-11 01:40:23 +0000242 size_t nFound = 0;
Anton Bikineev7ac13342022-01-24 21:25:15 +0000243 while (true) {
Tom Sepeza0de6da2021-11-11 01:40:23 +0000244 nFound = cFormat.find(cTableAd[i].js_mark, nFound);
245 if (nFound == std::wstring::npos)
246 break;
247
248 if (nFound != 0 && cFormat[nFound - 1] == L'%') {
249 ++nFound;
250 continue;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700251 }
Tom Sepeza0de6da2021-11-11 01:40:23 +0000252 cFormat.replace(nFound, 1,
Tom Sepezd6de8f02022-04-22 21:29:50 +0000253 WideString::FormatInteger(cTableAd[i].value).c_str());
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700254 }
Lei Zhang002d9362018-12-08 00:04:41 +0000255 }
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700256
Lei Zhang002d9362018-12-08 00:04:41 +0000257 struct tm time = {};
258 time.tm_year = year - 1900;
259 time.tm_mon = month - 1;
260 time.tm_mday = day;
261 time.tm_hour = hour;
262 time.tm_min = min;
263 time.tm_sec = sec;
tsepez86a61dc2016-03-25 10:00:11 -0700264
Lei Zhang002d9362018-12-08 00:04:41 +0000265 wchar_t buf[64] = {};
266 FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time);
267 cFormat = buf;
268 return CJS_Result::Success(pRuntime->NewString(cFormat.c_str()));
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700269}
270
Tom Sepez3a6d0582018-08-17 19:28:52 +0000271CJS_Result CJS_Util::printx(CJS_Runtime* pRuntime,
Tom Sepez90589d42023-11-01 18:24:30 +0000272 pdfium::span<v8::Local<v8::Value>> params) {
Dan Sinclair8f524d62017-10-25 13:30:31 -0400273 if (params.size() < 2)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000274 return CJS_Result::Failure(JSMessage::kParamError);
tsepezf3dc8c62016-08-10 06:29:29 -0700275
Tom Sepez3a6d0582018-08-17 19:28:52 +0000276 return CJS_Result::Success(
Tom Sepez9b86cb62018-11-01 19:11:15 +0000277 pRuntime->NewString(StringPrintx(pRuntime->ToWideString(params[0]),
278 pRuntime->ToWideString(params[1]))
Tom Sepezc62e8482018-06-20 20:32:04 +0000279 .AsStringView()));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700280}
281
Lei Zhang93fd8a82019-12-12 20:29:42 +0000282// static
Tom Sepez9b86cb62018-11-01 19:11:15 +0000283WideString CJS_Util::StringPrintx(const WideString& wsFormat,
284 const WideString& wsSource) {
Ryan Harrison275e2602017-09-18 14:23:18 -0400285 WideString wsResult;
Lei Zhang5d6714e2018-12-11 18:53:31 +0000286 wsResult.Reserve(wsFormat.GetLength());
Ryan Harrison875e98c2017-09-27 10:53:11 -0400287 size_t iSourceIdx = 0;
288 size_t iFormatIdx = 0;
tsepez4f1f41f2016-03-28 14:13:16 -0700289 CaseMode eCaseMode = kPreserveCase;
290 bool bEscaped = false;
291 while (iFormatIdx < wsFormat.GetLength()) {
292 if (bEscaped) {
293 bEscaped = false;
294 wsResult += wsFormat[iFormatIdx];
295 ++iFormatIdx;
296 continue;
297 }
298 switch (wsFormat[iFormatIdx]) {
299 case '\\': {
300 bEscaped = true;
301 ++iFormatIdx;
302 } break;
303 case '<': {
304 eCaseMode = kLowerCase;
305 ++iFormatIdx;
306 } break;
307 case '>': {
308 eCaseMode = kUpperCase;
309 ++iFormatIdx;
310 } break;
311 case '=': {
312 eCaseMode = kPreserveCase;
313 ++iFormatIdx;
314 } break;
315 case '?': {
316 if (iSourceIdx < wsSource.GetLength()) {
317 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
318 ++iSourceIdx;
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700319 }
tsepez4f1f41f2016-03-28 14:13:16 -0700320 ++iFormatIdx;
321 } break;
322 case 'X': {
323 if (iSourceIdx < wsSource.GetLength()) {
Ryan Harrison15c0fcc2018-03-12 15:20:04 +0000324 if (isascii(wsSource[iSourceIdx]) && isalnum(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700325 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
326 ++iFormatIdx;
327 }
328 ++iSourceIdx;
329 } else {
330 ++iFormatIdx;
331 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700332 } break;
333 case 'A': {
tsepez4f1f41f2016-03-28 14:13:16 -0700334 if (iSourceIdx < wsSource.GetLength()) {
Ryan Harrison735eda92018-03-12 15:44:36 +0000335 if (isascii(wsSource[iSourceIdx]) && isalpha(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700336 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
337 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700338 }
tsepez4f1f41f2016-03-28 14:13:16 -0700339 ++iSourceIdx;
340 } else {
341 ++iFormatIdx;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700342 }
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700343 } break;
344 case '9': {
tsepez4f1f41f2016-03-28 14:13:16 -0700345 if (iSourceIdx < wsSource.GetLength()) {
Lei Zhang4609c5d2018-12-07 20:10:54 +0000346 if (FXSYS_IsDecimalDigit(wsSource[iSourceIdx])) {
tsepez4f1f41f2016-03-28 14:13:16 -0700347 wsResult += wsSource[iSourceIdx];
348 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700349 }
tsepez4f1f41f2016-03-28 14:13:16 -0700350 ++iSourceIdx;
351 } else {
352 ++iFormatIdx;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700353 }
tsepez4f1f41f2016-03-28 14:13:16 -0700354 } break;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700355 case '*': {
tsepez4f1f41f2016-03-28 14:13:16 -0700356 if (iSourceIdx < wsSource.GetLength()) {
357 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
358 ++iSourceIdx;
359 } else {
360 ++iFormatIdx;
361 }
362 } break;
363 default: {
364 wsResult += wsFormat[iFormatIdx];
365 ++iFormatIdx;
366 } break;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700367 }
368 }
tsepez4f1f41f2016-03-28 14:13:16 -0700369 return wsResult;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700370}
371
Tom Sepez3a6d0582018-08-17 19:28:52 +0000372CJS_Result CJS_Util::scand(CJS_Runtime* pRuntime,
Tom Sepez90589d42023-11-01 18:24:30 +0000373 pdfium::span<v8::Local<v8::Value>> params) {
Lei Zhang574b5742017-03-30 12:41:55 -0700374 if (params.size() < 2)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000375 return CJS_Result::Failure(JSMessage::kParamError);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700376
dan sinclair80435cb2017-10-24 21:40:24 -0400377 WideString sFormat = pRuntime->ToWideString(params[0]);
378 WideString sDate = pRuntime->ToWideString(params[1]);
Tom Sepez86e5fbf2018-11-01 21:21:52 +0000379 double dDate = FX_GetDateTime();
Dan Sinclair8f524d62017-10-25 13:30:31 -0400380 if (sDate.GetLength() > 0)
Daniel Hosseinian4aa51a52021-10-22 17:32:43 +0000381 dDate = CJS_PublicMethods::ParseDateUsingFormat(pRuntime->GetIsolate(),
382 sDate, sFormat, nullptr);
Lei Zhang15fef792021-08-04 17:52:39 +0000383 if (isnan(dDate))
Tom Sepez3a6d0582018-08-17 19:28:52 +0000384 return CJS_Result::Success(pRuntime->NewUndefined());
Tom Sepez20736f72018-08-17 16:44:50 +0000385
Tom Sepez3a6d0582018-08-17 19:28:52 +0000386 return CJS_Result::Success(pRuntime->NewDate(dDate));
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700387}
388
Tom Sepez90589d42023-11-01 18:24:30 +0000389CJS_Result CJS_Util::byteToChar(CJS_Runtime* pRuntime,
390 pdfium::span<v8::Local<v8::Value>> params) {
Dan Sinclair8f524d62017-10-25 13:30:31 -0400391 if (params.size() < 1)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000392 return CJS_Result::Failure(JSMessage::kParamError);
tsepezf3dc8c62016-08-10 06:29:29 -0700393
dan sinclair80435cb2017-10-24 21:40:24 -0400394 int arg = pRuntime->ToInt32(params[0]);
Dan Sinclair8f524d62017-10-25 13:30:31 -0400395 if (arg < 0 || arg > 255)
Tom Sepez3a6d0582018-08-17 19:28:52 +0000396 return CJS_Result::Failure(JSMessage::kValueError);
tsepezf3dc8c62016-08-10 06:29:29 -0700397
Ryan Harrison275e2602017-09-18 14:23:18 -0400398 WideString wStr(static_cast<wchar_t>(arg));
Tom Sepez3a6d0582018-08-17 19:28:52 +0000399 return CJS_Result::Success(pRuntime->NewString(wStr.AsStringView()));
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700400}
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400401
Lei Zhang93fd8a82019-12-12 20:29:42 +0000402// static
Tom Sepez86390d72021-05-20 17:11:36 +0000403CJS_Util::DataType CJS_Util::ParseDataType(WideString* sFormat) {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000404 enum State { kBefore, kFlags, kWidth, kPrecision, kSpecifier, kAfter };
Tom Sepezffbc0d92017-07-17 09:29:05 -0700405
Tom Sepez86390d72021-05-20 17:11:36 +0000406 DataType result = DataType::kInvalid;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000407 State state = kBefore;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700408 size_t precision_digits = 0;
409 size_t i = 0;
Lei Zhang93fd8a82019-12-12 20:29:42 +0000410 while (i < sFormat->GetLength()) {
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400411 wchar_t c = (*sFormat)[i];
Tom Sepezffbc0d92017-07-17 09:29:05 -0700412 switch (state) {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000413 case kBefore:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700414 if (c == L'%')
Lei Zhang3f1101c2021-06-14 16:51:17 +0000415 state = kFlags;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700416 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000417 case kFlags:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700418 if (c == L'+' || c == L'-' || c == L'#' || c == L' ') {
419 // Stay in same state.
420 } else {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000421 state = kWidth;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700422 continue; // Re-process same character.
423 }
424 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000425 case kWidth:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700426 if (c == L'*')
Tom Sepez86390d72021-05-20 17:11:36 +0000427 return DataType::kInvalid;
Lei Zhang4609c5d2018-12-07 20:10:54 +0000428 if (FXSYS_IsDecimalDigit(c)) {
Tom Sepezffbc0d92017-07-17 09:29:05 -0700429 // Stay in same state.
430 } else if (c == L'.') {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000431 state = kPrecision;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700432 } else {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000433 state = kSpecifier;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700434 continue; // Re-process same character.
435 }
436 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000437 case kPrecision:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700438 if (c == L'*')
Tom Sepez86390d72021-05-20 17:11:36 +0000439 return DataType::kInvalid;
Lei Zhang4609c5d2018-12-07 20:10:54 +0000440 if (FXSYS_IsDecimalDigit(c)) {
Tom Sepezffbc0d92017-07-17 09:29:05 -0700441 // Stay in same state.
442 ++precision_digits;
443 } else {
Lei Zhang3f1101c2021-06-14 16:51:17 +0000444 state = kSpecifier;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700445 continue; // Re-process same character.
446 }
447 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000448 case kSpecifier:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700449 if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
450 c == L'u' || c == L'x' || c == L'X') {
Tom Sepez86390d72021-05-20 17:11:36 +0000451 result = DataType::kInt;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700452 } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' ||
453 c == L'G') {
Tom Sepez86390d72021-05-20 17:11:36 +0000454 result = DataType::kDouble;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700455 } else if (c == L's' || c == L'S') {
456 // Map s to S since we always deal internally with wchar_t strings.
457 // TODO(tsepez): Probably 100% borked. %S is not a standard
458 // conversion.
Lei Zhang93fd8a82019-12-12 20:29:42 +0000459 sFormat->SetAt(i, L'S');
Tom Sepez86390d72021-05-20 17:11:36 +0000460 result = DataType::kString;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700461 } else {
Tom Sepez86390d72021-05-20 17:11:36 +0000462 return DataType::kInvalid;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700463 }
Lei Zhang3f1101c2021-06-14 16:51:17 +0000464 state = kAfter;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700465 break;
Lei Zhang3f1101c2021-06-14 16:51:17 +0000466 case kAfter:
Tom Sepezffbc0d92017-07-17 09:29:05 -0700467 if (c == L'%')
Tom Sepez86390d72021-05-20 17:11:36 +0000468 return DataType::kInvalid;
Tom Sepezffbc0d92017-07-17 09:29:05 -0700469 // Stay in same state until string exhausted.
470 break;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400471 }
Tom Sepezffbc0d92017-07-17 09:29:05 -0700472 ++i;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400473 }
Tom Sepezffbc0d92017-07-17 09:29:05 -0700474 // See https://crbug.com/740166
Tom Sepez86390d72021-05-20 17:11:36 +0000475 if (result == DataType::kInt && precision_digits > 2)
476 return DataType::kInvalid;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400477
Tom Sepezffbc0d92017-07-17 09:29:05 -0700478 return result;
Henrique Nakashima3a4ebcc2017-07-14 14:24:42 -0400479}