Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cjs_util.cpp
Go to the documentation of this file.
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_util.h"
8
9#include <math.h>
10#include <time.h>
11
12#include <algorithm>
13#include <string>
14#include <vector>
15
16#include "build/build_config.h"
17#include "core/fxcrt/check_op.h"
18#include "core/fxcrt/compiler_specific.h"
19#include "core/fxcrt/fx_extension.h"
20#include "core/fxcrt/span.h"
21#include "fxjs/cjs_event_context.h"
22#include "fxjs/cjs_object.h"
23#include "fxjs/cjs_publicmethods.h"
24#include "fxjs/cjs_runtime.h"
25#include "fxjs/fx_date_helpers.h"
26#include "fxjs/fxv8.h"
27#include "fxjs/js_define.h"
28#include "fxjs/js_resources.h"
29#include "v8/include/v8-date.h"
30
31#if BUILDFLAG(IS_ANDROID)
32#include <ctype.h>
33#endif
34
35namespace {
36
37// Map PDF-style directives to equivalent wcsftime directives. Not
38// all have direct equivalents, though.
39struct TbConvert {
40 const wchar_t* lpszJSMark;
41 const wchar_t* lpszCppMark;
42};
43
44// Map PDF-style directives lacking direct wcsftime directives to
45// the value with which they will be replaced.
46struct TbConvertAdditional {
47 wchar_t js_mark;
48 int value;
49};
50
51const TbConvert kTbConvertTable[] = {
52 {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"},
53 {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"},
54 {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"},
55 {L"TT", L"%p"},
56#if BUILDFLAG(IS_WIN)
57 {L"tt", L"%p"}, {L"h", L"%#I"},
58#else
59 {L"tt", L"%P"}, {L"h", L"%l"},
60#endif
61};
62
63enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
64
65wchar_t TranslateCase(wchar_t input, CaseMode eMode) {
66 if (eMode == kLowerCase && FXSYS_iswupper(input))
67 return input | 0x20;
68 if (eMode == kUpperCase && FXSYS_iswlower(input))
69 return input & ~0x20;
70 return input;
71}
72
73} // namespace
74
75const JSMethodSpec CJS_Util::MethodSpecs[] = {
76 {"printd", printd_static},
77 {"printf", printf_static},
78 {"printx", printx_static},
79 {"scand", scand_static},
80 {"byteToChar", byteToChar_static}};
81
82uint32_t CJS_Util::ObjDefnID = 0;
83const char CJS_Util::kName[] = "util";
84
85// static
86uint32_t CJS_Util::GetObjDefnID() {
87 return ObjDefnID;
88}
89
90// static
91void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine) {
92 ObjDefnID = pEngine->DefineObj(CJS_Util::kName, FXJSOBJTYPE_STATIC,
93 JSConstructor<CJS_Util>, JSDestructor);
94 DefineMethods(pEngine, ObjDefnID, MethodSpecs);
95}
96
97CJS_Util::CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
98 : CJS_Object(pObject, pRuntime) {}
99
100CJS_Util::~CJS_Util() = default;
101
102CJS_Result CJS_Util::printf(CJS_Runtime* pRuntime,
103 pdfium::span<v8::Local<v8::Value>> params) {
104 const size_t num_params = params.size();
105 if (num_params < 1)
107
108 // Use 'S' as a sentinel to ensure we always have some text before the first
109 // format specifier.
110 WideString unsafe_fmt_string = L'S' + pRuntime->ToWideString(params[0]);
111 std::vector<WideString> unsafe_conversion_specifiers;
112
113 {
114 size_t offset = 0;
115 while (true) {
116 std::optional<size_t> offset_end =
117 unsafe_fmt_string.Find(L"%", offset + 1);
118 if (!offset_end.has_value()) {
119 unsafe_conversion_specifiers.push_back(
120 unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset));
121 break;
122 }
123
124 unsafe_conversion_specifiers.push_back(
125 unsafe_fmt_string.Substr(offset, offset_end.value() - offset));
126 offset = offset_end.value();
127 }
128 }
129
130 WideString result = unsafe_conversion_specifiers[0];
131 for (size_t i = 1; i < unsafe_conversion_specifiers.size(); ++i) {
132 WideString fmt = unsafe_conversion_specifiers[i];
133 if (i >= num_params) {
134 result += fmt;
135 continue;
136 }
137
138 WideString segment;
139 switch (ParseDataType(&fmt)) {
140 case DataType::kInt:
141 segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i]));
142 break;
143 case DataType::kDouble:
144 segment =
145 WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i]));
146 break;
147 case DataType::kString:
148 segment = WideString::Format(fmt.c_str(),
149 pRuntime->ToWideString(params[i]).c_str());
150 break;
151 default:
152 segment = WideString::Format(L"%ls", fmt.c_str());
153 break;
154 }
155 result += segment;
156 }
157
158 // Remove the 'S' sentinel introduced earlier.
159 DCHECK_EQ(L'S', result[0]);
160 auto result_view = result.AsStringView();
162 pRuntime->NewString(result_view.Last(result_view.GetLength() - 1)));
163}
164
165CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime,
166 pdfium::span<v8::Local<v8::Value>> params) {
167 const size_t iSize = params.size();
168 if (iSize < 2)
170
171 if (!fxv8::IsDate(params[1]))
173
174 v8::Local<v8::Date> v8_date = params[1].As<v8::Date>();
175 if (v8_date.IsEmpty() || isnan(pRuntime->ToDouble(v8_date)))
177
178 double date = FX_LocalTime(pRuntime->ToDouble(v8_date));
179 int year = FX_GetYearFromTime(date);
180 int month = FX_GetMonthFromTime(date) + 1; // One-based.
181 int day = FX_GetDayFromTime(date);
182 int hour = FX_GetHourFromTime(date);
183 int min = FX_GetMinFromTime(date);
184 int sec = FX_GetSecFromTime(date);
185
186 if (params[0]->IsNumber()) {
187 WideString swResult;
188 switch (pRuntime->ToInt32(params[0])) {
189 case 0:
190 swResult = WideString::Format(L"D:%04d%02d%02d%02d%02d%02d", year,
191 month, day, hour, min, sec);
192 break;
193 case 1:
194 swResult = WideString::Format(L"%04d.%02d.%02d %02d:%02d:%02d", year,
195 month, day, hour, min, sec);
196 break;
197 case 2:
198 swResult = WideString::Format(L"%04d/%02d/%02d %02d:%02d:%02d", year,
199 month, day, hour, min, sec);
200 break;
201 default:
203 }
204
205 return CJS_Result::Success(pRuntime->NewString(swResult.AsStringView()));
206 }
207
208 if (!params[0]->IsString())
210
211 // We don't support XFAPicture at the moment.
212 if (iSize > 2 && pRuntime->ToBoolean(params[2]))
214
215 // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
216 // pre-existing %-directives before inserting our own.
217 std::wstring cFormat = pRuntime->ToWideString(params[0]).c_str();
218 cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
219 cFormat.end());
220
221 for (const auto& conversion : kTbConvertTable) {
222 size_t nFound = 0;
223 while (true) {
224 nFound = cFormat.find(conversion.lpszJSMark, nFound);
225 if (nFound == std::wstring::npos) {
226 break;
227 }
228 cFormat.replace(nFound, wcslen(conversion.lpszJSMark),
229 conversion.lpszCppMark);
230 }
231 }
232
233 if (year < 0)
235
236 const TbConvertAdditional table_additional[] = {
237 {L'm', month}, {L'd', day},
238 {L'H', hour}, {L'h', hour > 12 ? hour - 12 : hour},
239 {L'M', min}, {L's', sec},
240 };
241
242 for (const auto& conversion : table_additional) {
243 size_t nFound = 0;
244 while (true) {
245 nFound = cFormat.find(conversion.js_mark, nFound);
246 if (nFound == std::wstring::npos) {
247 break;
248 }
249 if (nFound != 0 && cFormat[nFound - 1] == L'%') {
250 ++nFound;
251 continue;
252 }
253 cFormat.replace(nFound, 1,
254 WideString::FormatInteger(conversion.value).c_str());
255 }
256 }
257
258 struct tm time = {};
259 time.tm_year = year - 1900;
260 time.tm_mon = month - 1;
261 time.tm_mday = day;
262 time.tm_hour = hour;
263 time.tm_min = min;
264 time.tm_sec = sec;
265
266 wchar_t buf[64] = {};
267 UNSAFE_TODO(FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time));
268 cFormat = buf;
269 return CJS_Result::Success(pRuntime->NewString(cFormat.c_str()));
270}
271
272CJS_Result CJS_Util::printx(CJS_Runtime* pRuntime,
273 pdfium::span<v8::Local<v8::Value>> params) {
274 if (params.size() < 2)
276
277 return CJS_Result::Success(
278 pRuntime->NewString(StringPrintx(pRuntime->ToWideString(params[0]),
279 pRuntime->ToWideString(params[1]))
280 .AsStringView()));
281}
282
283// static
284WideString CJS_Util::StringPrintx(const WideString& wsFormat,
285 const WideString& wsSource) {
286 WideString wsResult;
287 wsResult.Reserve(wsFormat.GetLength());
288 size_t iSourceIdx = 0;
289 size_t iFormatIdx = 0;
290 CaseMode eCaseMode = kPreserveCase;
291 bool bEscaped = false;
292 while (iFormatIdx < wsFormat.GetLength()) {
293 if (bEscaped) {
294 bEscaped = false;
295 wsResult += wsFormat[iFormatIdx];
296 ++iFormatIdx;
297 continue;
298 }
299 switch (wsFormat[iFormatIdx]) {
300 case '\\': {
301 bEscaped = true;
302 ++iFormatIdx;
303 } break;
304 case '<': {
305 eCaseMode = kLowerCase;
306 ++iFormatIdx;
307 } break;
308 case '>': {
309 eCaseMode = kUpperCase;
310 ++iFormatIdx;
311 } break;
312 case '=': {
313 eCaseMode = kPreserveCase;
314 ++iFormatIdx;
315 } break;
316 case '?': {
317 if (iSourceIdx < wsSource.GetLength()) {
318 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
319 ++iSourceIdx;
320 }
321 ++iFormatIdx;
322 } break;
323 case 'X': {
324 if (iSourceIdx < wsSource.GetLength()) {
325 if (isascii(wsSource[iSourceIdx]) && isalnum(wsSource[iSourceIdx])) {
326 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
327 ++iFormatIdx;
328 }
329 ++iSourceIdx;
330 } else {
331 ++iFormatIdx;
332 }
333 } break;
334 case 'A': {
335 if (iSourceIdx < wsSource.GetLength()) {
336 if (isascii(wsSource[iSourceIdx]) && isalpha(wsSource[iSourceIdx])) {
337 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
338 ++iFormatIdx;
339 }
340 ++iSourceIdx;
341 } else {
342 ++iFormatIdx;
343 }
344 } break;
345 case '9': {
346 if (iSourceIdx < wsSource.GetLength()) {
347 if (FXSYS_IsDecimalDigit(wsSource[iSourceIdx])) {
348 wsResult += wsSource[iSourceIdx];
349 ++iFormatIdx;
350 }
351 ++iSourceIdx;
352 } else {
353 ++iFormatIdx;
354 }
355 } break;
356 case '*': {
357 if (iSourceIdx < wsSource.GetLength()) {
358 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
359 ++iSourceIdx;
360 } else {
361 ++iFormatIdx;
362 }
363 } break;
364 default: {
365 wsResult += wsFormat[iFormatIdx];
366 ++iFormatIdx;
367 } break;
368 }
369 }
370 return wsResult;
371}
372
373CJS_Result CJS_Util::scand(CJS_Runtime* pRuntime,
374 pdfium::span<v8::Local<v8::Value>> params) {
375 if (params.size() < 2)
377
378 WideString sFormat = pRuntime->ToWideString(params[0]);
379 WideString sDate = pRuntime->ToWideString(params[1]);
380 double dDate = FX_GetDateTime();
381 if (sDate.GetLength() > 0)
382 dDate = CJS_PublicMethods::ParseDateUsingFormat(pRuntime->GetIsolate(),
383 sDate, sFormat, nullptr);
384 if (isnan(dDate))
385 return CJS_Result::Success(pRuntime->NewUndefined());
386
387 return CJS_Result::Success(pRuntime->NewDate(dDate));
388}
389
390CJS_Result CJS_Util::byteToChar(CJS_Runtime* pRuntime,
391 pdfium::span<v8::Local<v8::Value>> params) {
392 if (params.size() < 1)
394
395 int arg = pRuntime->ToInt32(params[0]);
396 if (arg < 0 || arg > 255)
398
399 WideString wStr(static_cast<wchar_t>(arg));
400 return CJS_Result::Success(pRuntime->NewString(wStr.AsStringView()));
401}
402
403// static
404CJS_Util::DataType CJS_Util::ParseDataType(WideString* sFormat) {
405 enum State { kBefore, kFlags, kWidth, kPrecision, kSpecifier, kAfter };
406
408 State state = kBefore;
409 size_t precision_digits = 0;
410 size_t i = 0;
411 while (i < sFormat->GetLength()) {
412 wchar_t c = (*sFormat)[i];
413 switch (state) {
414 case kBefore:
415 if (c == L'%')
416 state = kFlags;
417 break;
418 case kFlags:
419 if (c == L'+' || c == L'-' || c == L'#' || c == L' ') {
420 // Stay in same state.
421 } else {
422 state = kWidth;
423 continue; // Re-process same character.
424 }
425 break;
426 case kWidth:
427 if (c == L'*')
428 return DataType::kInvalid;
430 // Stay in same state.
431 } else if (c == L'.') {
432 state = kPrecision;
433 } else {
434 state = kSpecifier;
435 continue; // Re-process same character.
436 }
437 break;
438 case kPrecision:
439 if (c == L'*')
440 return DataType::kInvalid;
442 // Stay in same state.
443 ++precision_digits;
444 } else {
445 state = kSpecifier;
446 continue; // Re-process same character.
447 }
448 break;
449 case kSpecifier:
450 if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
451 c == L'u' || c == L'x' || c == L'X') {
452 result = DataType::kInt;
453 } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' ||
454 c == L'G') {
455 result = DataType::kDouble;
456 } else if (c == L's' || c == L'S') {
457 // Map s to S since we always deal internally with wchar_t strings.
458 // TODO(tsepez): Probably 100% borked. %S is not a standard
459 // conversion.
460 sFormat->SetAt(i, L'S');
461 result = DataType::kString;
462 } else {
463 return DataType::kInvalid;
464 }
465 state = kAfter;
466 break;
467 case kAfter:
468 if (c == L'%')
469 return DataType::kInvalid;
470 // Stay in same state until string exhausted.
471 break;
472 }
473 ++i;
474 }
475 // See https://crbug.com/740166
476 if (result == DataType::kInt && precision_digits > 2)
477 return DataType::kInvalid;
478
479 return result;
480}
@ FXJSOBJTYPE_STATIC
#define DCHECK_EQ(x, y)
Definition check_op.h:17
static CJS_Result Success()
Definition cjs_result.h:28
static CJS_Result Failure(JSMessage id)
Definition cjs_result.h:35
CJS_Util(v8::Local< v8::Object > pObject, CJS_Runtime *pRuntime)
Definition cjs_util.cpp:97
~CJS_Util() override
static uint32_t GetObjDefnID()
Definition cjs_util.cpp:86
static void DefineJSObjects(CFXJS_Engine *pEngine)
Definition cjs_util.cpp:91
static DataType ParseDataType(WideString *sFormat)
Definition cjs_util.cpp:404
static WideString StringPrintx(const WideString &cFormat, const WideString &cSource)
Definition cjs_util.cpp:284
WideString & operator+=(const WideString &str)
WideString(wchar_t ch)
static WideString Format(const wchar_t *pFormat,...)
WideString & operator=(WideString &&that) noexcept
static WideString FormatInteger(int i)
#define UNSAFE_TODO(...)
bool FXSYS_IsDecimalDigit(wchar_t c)
bool FXSYS_iswlower(int32_t c)
bool FXSYS_iswupper(int32_t c)
#define FXSYS_wcsftime
Definition fx_system.h:74
JSMessage
@ kNotSupportedError
@ kSecondParamNotDateError
@ kSecondParamInvalidDateError
int FX_GetMonthFromTime(double dt)
int FX_GetYearFromTime(double dt)
double FX_GetDateTime()
int FX_GetDayFromTime(double dt)
int FX_GetSecFromTime(double dt)
int FX_GetHourFromTime(double dt)
int FX_GetMinFromTime(double dt)
fxcrt::WideString WideString
Definition widestring.h:207