From 5bfac68b530d0dae99dfde648ccadd667c238b0d Mon Sep 17 00:00:00 2001 From: Dan Sinclair Date: Mon, 30 Oct 2017 19:51:03 +0000 Subject: Rename util to cjs_util This CL renames util.{cpp|h} to cjs_util.{cpp|h} to match the file contents. Change-Id: Ia1bcbced0d817e2334310a25b09f9ec56822d600 Reviewed-on: https://pdfium-review.googlesource.com/17046 Commit-Queue: dsinclair Reviewed-by: Tom Sepez --- BUILD.gn | 6 +- fpdfsdk/javascript/cjs_publicmethods.cpp | 2 +- fpdfsdk/javascript/cjs_runtime.cpp | 2 +- fpdfsdk/javascript/cjs_util.cpp | 468 +++++++++++++++++++++++++++++++ fpdfsdk/javascript/cjs_util.h | 63 +++++ fpdfsdk/javascript/cjs_util_unittest.cpp | 113 ++++++++ fpdfsdk/javascript/util.cpp | 468 ------------------------------- fpdfsdk/javascript/util.h | 63 ----- fpdfsdk/javascript/util_unittest.cpp | 113 -------- 9 files changed, 649 insertions(+), 649 deletions(-) create mode 100644 fpdfsdk/javascript/cjs_util.cpp create mode 100644 fpdfsdk/javascript/cjs_util.h create mode 100644 fpdfsdk/javascript/cjs_util_unittest.cpp delete mode 100644 fpdfsdk/javascript/util.cpp delete mode 100644 fpdfsdk/javascript/util.h delete mode 100644 fpdfsdk/javascript/util_unittest.cpp diff --git a/BUILD.gn b/BUILD.gn index cedfbca4fe..5550790462 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1254,14 +1254,14 @@ static_library("javascript") { "fpdfsdk/javascript/cjs_style.h", "fpdfsdk/javascript/cjs_timerobj.cpp", "fpdfsdk/javascript/cjs_timerobj.h", + "fpdfsdk/javascript/cjs_util.cpp", + "fpdfsdk/javascript/cjs_util.h", "fpdfsdk/javascript/cjs_zoomtype.cpp", "fpdfsdk/javascript/cjs_zoomtype.h", "fpdfsdk/javascript/global_timer.cpp", "fpdfsdk/javascript/global_timer.h", "fpdfsdk/javascript/js_resources.cpp", "fpdfsdk/javascript/js_resources.h", - "fpdfsdk/javascript/util.cpp", - "fpdfsdk/javascript/util.h", ] deps += [ ":fxjs" ] configs += [ "//v8:external_startup_data" ] @@ -2015,7 +2015,7 @@ test("pdfium_unittests") { if (pdf_enable_v8) { sources += [ "fpdfsdk/javascript/cjs_publicmethods_unittest.cpp", - "fpdfsdk/javascript/util_unittest.cpp", + "fpdfsdk/javascript/cjs_util_unittest.cpp", ] include_dirs += [ "//v8", diff --git a/fpdfsdk/javascript/cjs_publicmethods.cpp b/fpdfsdk/javascript/cjs_publicmethods.cpp index fb4cc2c3ee..3a309d4225 100644 --- a/fpdfsdk/javascript/cjs_publicmethods.cpp +++ b/fpdfsdk/javascript/cjs_publicmethods.cpp @@ -26,8 +26,8 @@ #include "fpdfsdk/javascript/cjs_field.h" #include "fpdfsdk/javascript/cjs_object.h" #include "fpdfsdk/javascript/cjs_runtime.h" +#include "fpdfsdk/javascript/cjs_util.h" #include "fpdfsdk/javascript/js_resources.h" -#include "fpdfsdk/javascript/util.h" #define DOUBLE_CORRECT 0.000000000000001 diff --git a/fpdfsdk/javascript/cjs_runtime.cpp b/fpdfsdk/javascript/cjs_runtime.cpp index ae66e99ca4..9e09ad2996 100644 --- a/fpdfsdk/javascript/cjs_runtime.cpp +++ b/fpdfsdk/javascript/cjs_runtime.cpp @@ -37,8 +37,8 @@ #include "fpdfsdk/javascript/cjs_scalewhen.h" #include "fpdfsdk/javascript/cjs_style.h" #include "fpdfsdk/javascript/cjs_timerobj.h" +#include "fpdfsdk/javascript/cjs_util.h" #include "fpdfsdk/javascript/cjs_zoomtype.h" -#include "fpdfsdk/javascript/util.h" #include "public/fpdf_formfill.h" #include "third_party/base/stl_util.h" diff --git a/fpdfsdk/javascript/cjs_util.cpp b/fpdfsdk/javascript/cjs_util.cpp new file mode 100644 index 0000000000..6eb52f13fe --- /dev/null +++ b/fpdfsdk/javascript/cjs_util.cpp @@ -0,0 +1,468 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "fpdfsdk/javascript/cjs_util.h" + +#include + +#include +#include +#include +#include +#include + +#include "core/fxcrt/fx_extension.h" +#include "fpdfsdk/javascript/JS_Define.h" +#include "fpdfsdk/javascript/cjs_event_context.h" +#include "fpdfsdk/javascript/cjs_eventhandler.h" +#include "fpdfsdk/javascript/cjs_object.h" +#include "fpdfsdk/javascript/cjs_publicmethods.h" +#include "fpdfsdk/javascript/cjs_runtime.h" +#include "fpdfsdk/javascript/js_resources.h" + +#if _FX_OS_ == _FX_OS_ANDROID_ +#include +#endif + +namespace { + +// Map PDF-style directives to equivalent wcsftime directives. Not +// all have direct equivalents, though. +struct TbConvert { + const wchar_t* lpszJSMark; + const wchar_t* lpszCppMark; +}; + +// Map PDF-style directives lacking direct wcsftime directives to +// the value with which they will be replaced. +struct TbConvertAdditional { + const wchar_t* lpszJSMark; + int iValue; +}; + +const TbConvert TbConvertTable[] = { + {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"}, + {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"}, + {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"}, + {L"TT", L"%p"}, +#if defined(_WIN32) + {L"tt", L"%p"}, {L"h", L"%#I"}, +#else + {L"tt", L"%P"}, {L"h", L"%l"}, +#endif +}; + +} // namespace + +const JSMethodSpec CJS_Util::MethodSpecs[] = { + {"printd", printd_static}, {"printf", printf_static}, + {"printx", printx_static}, {"scand", scand_static}, + {"byteToChar", byteToChar_static}, {0, 0}}; + +int CJS_Util::ObjDefnID = -1; + +// static +void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine) { + ObjDefnID = + pEngine->DefineObj("util", FXJSOBJTYPE_STATIC, + JSConstructor, JSDestructor); + DefineMethods(pEngine, ObjDefnID, MethodSpecs); +} + +util::util(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {} + +util::~util() {} + +CJS_Return util::printf(CJS_Runtime* pRuntime, + const std::vector>& params) { + const size_t iSize = params.size(); + if (iSize < 1) + return CJS_Return(false); + + std::wstring unsafe_fmt_string(pRuntime->ToWideString(params[0]).c_str()); + std::vector unsafe_conversion_specifiers; + int iOffset = 0; + int iOffend = 0; + unsafe_fmt_string.insert(unsafe_fmt_string.begin(), L'S'); + while (iOffset != -1) { + iOffend = unsafe_fmt_string.find(L"%", iOffset + 1); + std::wstring strSub; + if (iOffend == -1) + strSub = unsafe_fmt_string.substr(iOffset); + else + strSub = unsafe_fmt_string.substr(iOffset, iOffend - iOffset); + unsafe_conversion_specifiers.push_back(strSub); + iOffset = iOffend; + } + + std::wstring c_strResult; + for (size_t iIndex = 0; iIndex < unsafe_conversion_specifiers.size(); + ++iIndex) { + std::wstring c_strFormat = unsafe_conversion_specifiers[iIndex]; + if (iIndex == 0) { + c_strResult = c_strFormat; + continue; + } + + if (iIndex >= iSize) { + c_strResult += c_strFormat; + continue; + } + + WideString strSegment; + switch (ParseDataType(&c_strFormat)) { + case UTIL_INT: + strSegment.Format(c_strFormat.c_str(), + pRuntime->ToInt32(params[iIndex])); + break; + case UTIL_DOUBLE: + strSegment.Format(c_strFormat.c_str(), + pRuntime->ToDouble(params[iIndex])); + break; + case UTIL_STRING: + strSegment.Format(c_strFormat.c_str(), + pRuntime->ToWideString(params[iIndex]).c_str()); + break; + default: + strSegment.Format(L"%ls", c_strFormat.c_str()); + break; + } + c_strResult += strSegment.c_str(); + } + + c_strResult.erase(c_strResult.begin()); + return CJS_Return(pRuntime->NewString(c_strResult.c_str())); +} + +CJS_Return util::printd(CJS_Runtime* pRuntime, + const std::vector>& params) { + const size_t iSize = params.size(); + if (iSize < 2) + return CJS_Return(false); + + if (params[1].IsEmpty() || !params[1]->IsDate()) + return CJS_Return(JSGetStringFromID(IDS_STRING_JSPRINT1)); + + v8::Local v8_date = params[1].As(); + if (v8_date.IsEmpty() || std::isnan(pRuntime->ToDouble(v8_date))) + return CJS_Return(JSGetStringFromID(IDS_STRING_JSPRINT2)); + + double date = JS_LocalTime(pRuntime->ToDouble(v8_date)); + int year = JS_GetYearFromTime(date); + int month = JS_GetMonthFromTime(date) + 1; // One-based. + int day = JS_GetDayFromTime(date); + int hour = JS_GetHourFromTime(date); + int min = JS_GetMinFromTime(date); + int sec = JS_GetSecFromTime(date); + + if (params[0]->IsNumber()) { + WideString swResult; + switch (pRuntime->ToInt32(params[0])) { + case 0: + swResult.Format(L"D:%04d%02d%02d%02d%02d%02d", year, month, day, hour, + min, sec); + break; + case 1: + swResult.Format(L"%04d.%02d.%02d %02d:%02d:%02d", year, month, day, + hour, min, sec); + break; + case 2: + swResult.Format(L"%04d/%02d/%02d %02d:%02d:%02d", year, month, day, + hour, min, sec); + break; + default: + return CJS_Return(JSGetStringFromID(IDS_STRING_JSVALUEERROR)); + } + + return CJS_Return(pRuntime->NewString(swResult.c_str())); + } + + if (params[0]->IsString()) { + // We don't support XFAPicture at the moment. + if (iSize > 2 && pRuntime->ToBoolean(params[2])) + return CJS_Return(JSGetStringFromID(IDS_STRING_JSNOTSUPPORT)); + + // Convert PDF-style format specifiers to wcsftime specifiers. Remove any + // pre-existing %-directives before inserting our own. + std::basic_string cFormat = + pRuntime->ToWideString(params[0]).c_str(); + cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'), + cFormat.end()); + + for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) { + int iStart = 0; + int iEnd; + while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) != + -1) { + cFormat.replace(iEnd, wcslen(TbConvertTable[i].lpszJSMark), + TbConvertTable[i].lpszCppMark); + iStart = iEnd; + } + } + + if (year < 0) + return CJS_Return(JSGetStringFromID(IDS_STRING_JSVALUEERROR)); + + static const TbConvertAdditional cTableAd[] = { + {L"m", month}, {L"d", day}, + {L"H", hour}, {L"h", hour > 12 ? hour - 12 : hour}, + {L"M", min}, {L"s", sec}, + }; + + for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) { + WideString sValue; + sValue.Format(L"%d", cTableAd[i].iValue); + + int iStart = 0; + int iEnd; + while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) { + if (iEnd > 0) { + if (cFormat[iEnd - 1] == L'%') { + iStart = iEnd + 1; + continue; + } + } + cFormat.replace(iEnd, wcslen(cTableAd[i].lpszJSMark), sValue.c_str()); + iStart = iEnd; + } + } + + struct tm time = {}; + time.tm_year = year - 1900; + time.tm_mon = month - 1; + time.tm_mday = day; + time.tm_hour = hour; + time.tm_min = min; + time.tm_sec = sec; + + wchar_t buf[64] = {}; + FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time); + cFormat = buf; + return CJS_Return(pRuntime->NewString(cFormat.c_str())); + } + + return CJS_Return(JSGetStringFromID(IDS_STRING_JSTYPEERROR)); +} + +CJS_Return util::printx(CJS_Runtime* pRuntime, + const std::vector>& params) { + if (params.size() < 2) + return CJS_Return(JSGetStringFromID(IDS_STRING_JSPARAMERROR)); + + return CJS_Return( + pRuntime->NewString(printx(pRuntime->ToWideString(params[0]), + pRuntime->ToWideString(params[1])) + .c_str())); +} + +enum CaseMode { kPreserveCase, kUpperCase, kLowerCase }; + +static wchar_t TranslateCase(wchar_t input, CaseMode eMode) { + if (eMode == kLowerCase && FXSYS_isupper(input)) + return input | 0x20; + if (eMode == kUpperCase && FXSYS_islower(input)) + return input & ~0x20; + return input; +} + +WideString util::printx(const WideString& wsFormat, + const WideString& wsSource) { + WideString wsResult; + size_t iSourceIdx = 0; + size_t iFormatIdx = 0; + CaseMode eCaseMode = kPreserveCase; + bool bEscaped = false; + while (iFormatIdx < wsFormat.GetLength()) { + if (bEscaped) { + bEscaped = false; + wsResult += wsFormat[iFormatIdx]; + ++iFormatIdx; + continue; + } + switch (wsFormat[iFormatIdx]) { + case '\\': { + bEscaped = true; + ++iFormatIdx; + } break; + case '<': { + eCaseMode = kLowerCase; + ++iFormatIdx; + } break; + case '>': { + eCaseMode = kUpperCase; + ++iFormatIdx; + } break; + case '=': { + eCaseMode = kPreserveCase; + ++iFormatIdx; + } break; + case '?': { + if (iSourceIdx < wsSource.GetLength()) { + wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); + ++iSourceIdx; + } + ++iFormatIdx; + } break; + case 'X': { + if (iSourceIdx < wsSource.GetLength()) { + if (FXSYS_iswalnum(wsSource[iSourceIdx])) { + wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); + ++iFormatIdx; + } + ++iSourceIdx; + } else { + ++iFormatIdx; + } + } break; + case 'A': { + if (iSourceIdx < wsSource.GetLength()) { + if (FXSYS_iswalpha(wsSource[iSourceIdx])) { + wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); + ++iFormatIdx; + } + ++iSourceIdx; + } else { + ++iFormatIdx; + } + } break; + case '9': { + if (iSourceIdx < wsSource.GetLength()) { + if (std::iswdigit(wsSource[iSourceIdx])) { + wsResult += wsSource[iSourceIdx]; + ++iFormatIdx; + } + ++iSourceIdx; + } else { + ++iFormatIdx; + } + } break; + case '*': { + if (iSourceIdx < wsSource.GetLength()) { + wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); + ++iSourceIdx; + } else { + ++iFormatIdx; + } + } break; + default: { + wsResult += wsFormat[iFormatIdx]; + ++iFormatIdx; + } break; + } + } + return wsResult; +} + +CJS_Return util::scand(CJS_Runtime* pRuntime, + const std::vector>& params) { + if (params.size() < 2) + return CJS_Return(false); + + WideString sFormat = pRuntime->ToWideString(params[0]); + WideString sDate = pRuntime->ToWideString(params[1]); + double dDate = JS_GetDateTime(); + if (sDate.GetLength() > 0) + dDate = CJS_PublicMethods::MakeRegularDate(sDate, sFormat, nullptr); + + if (std::isnan(dDate)) + return CJS_Return(pRuntime->NewUndefined()); + return CJS_Return(pRuntime->NewDate(dDate)); +} + +CJS_Return util::byteToChar(CJS_Runtime* pRuntime, + const std::vector>& params) { + if (params.size() < 1) + return CJS_Return(JSGetStringFromID(IDS_STRING_JSPARAMERROR)); + + int arg = pRuntime->ToInt32(params[0]); + if (arg < 0 || arg > 255) + return CJS_Return(JSGetStringFromID(IDS_STRING_JSVALUEERROR)); + + WideString wStr(static_cast(arg)); + return CJS_Return(pRuntime->NewString(wStr.c_str())); +} + +// Ensure that sFormat contains at most one well-understood printf formatting +// directive which is safe to use with a single argument, and return the type +// of argument expected, or -1 otherwise. If -1 is returned, it is NOT safe +// to use sFormat with printf() and it must be copied byte-by-byte. +int util::ParseDataType(std::wstring* sFormat) { + enum State { BEFORE, FLAGS, WIDTH, PRECISION, SPECIFIER, AFTER }; + + int result = -1; + State state = BEFORE; + size_t precision_digits = 0; + size_t i = 0; + while (i < sFormat->length()) { + wchar_t c = (*sFormat)[i]; + switch (state) { + case BEFORE: + if (c == L'%') + state = FLAGS; + break; + case FLAGS: + if (c == L'+' || c == L'-' || c == L'#' || c == L' ') { + // Stay in same state. + } else { + state = WIDTH; + continue; // Re-process same character. + } + break; + case WIDTH: + if (c == L'*') + return -1; + if (std::iswdigit(c)) { + // Stay in same state. + } else if (c == L'.') { + state = PRECISION; + } else { + state = SPECIFIER; + continue; // Re-process same character. + } + break; + case PRECISION: + if (c == L'*') + return -1; + if (std::iswdigit(c)) { + // Stay in same state. + ++precision_digits; + } else { + state = SPECIFIER; + continue; // Re-process same character. + } + break; + case SPECIFIER: + if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' || + c == L'u' || c == L'x' || c == L'X') { + result = UTIL_INT; + } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' || + c == L'G') { + result = UTIL_DOUBLE; + } else if (c == L's' || c == L'S') { + // Map s to S since we always deal internally with wchar_t strings. + // TODO(tsepez): Probably 100% borked. %S is not a standard + // conversion. + (*sFormat)[i] = L'S'; + result = UTIL_STRING; + } else { + return -1; + } + state = AFTER; + break; + case AFTER: + if (c == L'%') + return -1; + // Stay in same state until string exhausted. + break; + } + ++i; + } + // See https://crbug.com/740166 + if (result == UTIL_INT && precision_digits > 2) + return -1; + + return result; +} diff --git a/fpdfsdk/javascript/cjs_util.h b/fpdfsdk/javascript/cjs_util.h new file mode 100644 index 0000000000..f3d808bcb6 --- /dev/null +++ b/fpdfsdk/javascript/cjs_util.h @@ -0,0 +1,63 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef FPDFSDK_JAVASCRIPT_CJS_UTIL_H_ +#define FPDFSDK_JAVASCRIPT_CJS_UTIL_H_ + +#include +#include + +#include "fpdfsdk/javascript/JS_Define.h" + +// Return values for ParseDataType() below. +#define UTIL_INT 0 +#define UTIL_DOUBLE 1 +#define UTIL_STRING 2 + +class util : public CJS_EmbedObj { + public: + explicit util(CJS_Object* pJSObject); + ~util() override; + + CJS_Return printd(CJS_Runtime* pRuntime, + const std::vector>& params); + CJS_Return printf(CJS_Runtime* pRuntime, + const std::vector>& params); + CJS_Return printx(CJS_Runtime* pRuntime, + const std::vector>& params); + CJS_Return scand(CJS_Runtime* pRuntime, + const std::vector>& params); + CJS_Return byteToChar(CJS_Runtime* pRuntime, + const std::vector>& params); + + static WideString printx(const WideString& cFormat, + const WideString& cSource); + + private: + friend class CJS_Util_ParseDataType_Test; + + static int ParseDataType(std::wstring* sFormat); +}; + +class CJS_Util : public CJS_Object { + public: + static void DefineJSObjects(CFXJS_Engine* pEngine); + + explicit CJS_Util(v8::Local pObject) : CJS_Object(pObject) {} + ~CJS_Util() override {} + + JS_STATIC_METHOD(printd, util); + JS_STATIC_METHOD(printf, util); + JS_STATIC_METHOD(printx, util); + JS_STATIC_METHOD(scand, util); + JS_STATIC_METHOD(byteToChar, util); + + private: + static int ObjDefnID; + static const JSMethodSpec MethodSpecs[]; +}; + +#endif // FPDFSDK_JAVASCRIPT_CJS_UTIL_H_ diff --git a/fpdfsdk/javascript/cjs_util_unittest.cpp b/fpdfsdk/javascript/cjs_util_unittest.cpp new file mode 100644 index 0000000000..bfd5b58abd --- /dev/null +++ b/fpdfsdk/javascript/cjs_util_unittest.cpp @@ -0,0 +1,113 @@ +// Copyright 2017 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fpdfsdk/javascript/cjs_util.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/test_support.h" + +TEST(CJS_Util, ParseDataType) { + struct ParseDataTypeCase { + const wchar_t* const input_string; + const int expected; + }; + + // Commented out tests follow the spec but are not passing. + const ParseDataTypeCase cases[] = { + // Not conversions + {L"", -1}, + {L"d", -1}, + + // Simple cases + {L"%d", UTIL_INT}, + {L"%x", UTIL_INT}, + {L"%f", UTIL_DOUBLE}, + {L"%s", UTIL_STRING}, + + // nDecSep Not implemented + // {L"%,0d", UTIL_INT}, + // {L"%,1d", UTIL_INT}, + // {L"%,2d", UTIL_INT}, + // {L"%,3d", UTIL_INT}, + // {L"%,4d", -1}, + // {L"%,d", -1}, + + // cFlags("+ 0#"") are only valid for numeric conversions. + {L"%+d", UTIL_INT}, + {L"%+x", UTIL_INT}, + {L"%+f", UTIL_DOUBLE}, + // {L"%+s", -1}, + {L"% d", UTIL_INT}, + {L"% x", UTIL_INT}, + {L"% f", UTIL_DOUBLE}, + // {L"% s", -1}, + {L"%0d", UTIL_INT}, + {L"%0x", UTIL_INT}, + {L"%0f", UTIL_DOUBLE}, + // {L"%0s", -1}, + {L"%#d", UTIL_INT}, + {L"%#x", UTIL_INT}, + {L"%#f", UTIL_DOUBLE}, + // {L"%#s", -1}, + + // nWidth should work. for all conversions, can be combined with cFlags=0 + // for numbers. + {L"%5d", UTIL_INT}, + {L"%05d", UTIL_INT}, + {L"%5x", UTIL_INT}, + {L"%05x", UTIL_INT}, + {L"%5f", UTIL_DOUBLE}, + {L"%05f", UTIL_DOUBLE}, + {L"%5s", UTIL_STRING}, + // {L"%05s", -1}, + + // nPrecision should only work for float + // {L"%.5d", -1}, + // {L"%.5x", -1}, + {L"%.5f", UTIL_DOUBLE}, + // {L"%.5s", -1}, + // {L"%.14d", -1}, + // {L"%.14x", -1}, + {L"%.14f", UTIL_DOUBLE}, + // {L"%.14s", -1}, + // {L"%.f", -1}, + + // See https://crbug.com/740166 + // nPrecision too large (> 260) causes crashes in Windows. + // Avoid this by limiting to two digits + {L"%.1d", UTIL_INT}, + {L"%.10d", UTIL_INT}, + {L"%.100d", -1}, + + // Unexpected characters + {L"%ad", -1}, + {L"%bx", -1}, + // {L"%cf", -1}, + // {L"%es", -1}, + // {L"%gd", -1}, + {L"%hx", -1}, + // {L"%if", -1}, + {L"%js", -1}, + {L"%@d", -1}, + {L"%~x", -1}, + {L"%[f", -1}, + {L"%\0s", -1}, + {L"%\nd", -1}, + {L"%\rx", -1}, + // {L"%%f", -1}, + // {L"% s", -1}, + + // Combine multiple valid components + {L"%+6d", UTIL_INT}, + {L"% 7x", UTIL_INT}, + {L"%#9.3f", UTIL_DOUBLE}, + {L"%10s", UTIL_STRING}, + }; + + for (size_t i = 0; i < FX_ArraySize(cases); i++) { + std::wstring input(cases[i].input_string); + EXPECT_EQ(cases[i].expected, util::ParseDataType(&input)) + << cases[i].input_string; + } +} diff --git a/fpdfsdk/javascript/util.cpp b/fpdfsdk/javascript/util.cpp deleted file mode 100644 index 3dd5eb6d91..0000000000 --- a/fpdfsdk/javascript/util.cpp +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright 2014 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com - -#include "fpdfsdk/javascript/util.h" - -#include - -#include -#include -#include -#include -#include - -#include "core/fxcrt/fx_extension.h" -#include "fpdfsdk/javascript/JS_Define.h" -#include "fpdfsdk/javascript/cjs_event_context.h" -#include "fpdfsdk/javascript/cjs_eventhandler.h" -#include "fpdfsdk/javascript/cjs_object.h" -#include "fpdfsdk/javascript/cjs_publicmethods.h" -#include "fpdfsdk/javascript/cjs_runtime.h" -#include "fpdfsdk/javascript/js_resources.h" - -#if _FX_OS_ == _FX_OS_ANDROID_ -#include -#endif - -namespace { - -// Map PDF-style directives to equivalent wcsftime directives. Not -// all have direct equivalents, though. -struct TbConvert { - const wchar_t* lpszJSMark; - const wchar_t* lpszCppMark; -}; - -// Map PDF-style directives lacking direct wcsftime directives to -// the value with which they will be replaced. -struct TbConvertAdditional { - const wchar_t* lpszJSMark; - int iValue; -}; - -const TbConvert TbConvertTable[] = { - {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"}, - {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"}, - {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"}, - {L"TT", L"%p"}, -#if defined(_WIN32) - {L"tt", L"%p"}, {L"h", L"%#I"}, -#else - {L"tt", L"%P"}, {L"h", L"%l"}, -#endif -}; - -} // namespace - -const JSMethodSpec CJS_Util::MethodSpecs[] = { - {"printd", printd_static}, {"printf", printf_static}, - {"printx", printx_static}, {"scand", scand_static}, - {"byteToChar", byteToChar_static}, {0, 0}}; - -int CJS_Util::ObjDefnID = -1; - -// static -void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine) { - ObjDefnID = - pEngine->DefineObj("util", FXJSOBJTYPE_STATIC, - JSConstructor, JSDestructor); - DefineMethods(pEngine, ObjDefnID, MethodSpecs); -} - -util::util(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {} - -util::~util() {} - -CJS_Return util::printf(CJS_Runtime* pRuntime, - const std::vector>& params) { - const size_t iSize = params.size(); - if (iSize < 1) - return CJS_Return(false); - - std::wstring unsafe_fmt_string(pRuntime->ToWideString(params[0]).c_str()); - std::vector unsafe_conversion_specifiers; - int iOffset = 0; - int iOffend = 0; - unsafe_fmt_string.insert(unsafe_fmt_string.begin(), L'S'); - while (iOffset != -1) { - iOffend = unsafe_fmt_string.find(L"%", iOffset + 1); - std::wstring strSub; - if (iOffend == -1) - strSub = unsafe_fmt_string.substr(iOffset); - else - strSub = unsafe_fmt_string.substr(iOffset, iOffend - iOffset); - unsafe_conversion_specifiers.push_back(strSub); - iOffset = iOffend; - } - - std::wstring c_strResult; - for (size_t iIndex = 0; iIndex < unsafe_conversion_specifiers.size(); - ++iIndex) { - std::wstring c_strFormat = unsafe_conversion_specifiers[iIndex]; - if (iIndex == 0) { - c_strResult = c_strFormat; - continue; - } - - if (iIndex >= iSize) { - c_strResult += c_strFormat; - continue; - } - - WideString strSegment; - switch (ParseDataType(&c_strFormat)) { - case UTIL_INT: - strSegment.Format(c_strFormat.c_str(), - pRuntime->ToInt32(params[iIndex])); - break; - case UTIL_DOUBLE: - strSegment.Format(c_strFormat.c_str(), - pRuntime->ToDouble(params[iIndex])); - break; - case UTIL_STRING: - strSegment.Format(c_strFormat.c_str(), - pRuntime->ToWideString(params[iIndex]).c_str()); - break; - default: - strSegment.Format(L"%ls", c_strFormat.c_str()); - break; - } - c_strResult += strSegment.c_str(); - } - - c_strResult.erase(c_strResult.begin()); - return CJS_Return(pRuntime->NewString(c_strResult.c_str())); -} - -CJS_Return util::printd(CJS_Runtime* pRuntime, - const std::vector>& params) { - const size_t iSize = params.size(); - if (iSize < 2) - return CJS_Return(false); - - if (params[1].IsEmpty() || !params[1]->IsDate()) - return CJS_Return(JSGetStringFromID(IDS_STRING_JSPRINT1)); - - v8::Local v8_date = params[1].As(); - if (v8_date.IsEmpty() || std::isnan(pRuntime->ToDouble(v8_date))) - return CJS_Return(JSGetStringFromID(IDS_STRING_JSPRINT2)); - - double date = JS_LocalTime(pRuntime->ToDouble(v8_date)); - int year = JS_GetYearFromTime(date); - int month = JS_GetMonthFromTime(date) + 1; // One-based. - int day = JS_GetDayFromTime(date); - int hour = JS_GetHourFromTime(date); - int min = JS_GetMinFromTime(date); - int sec = JS_GetSecFromTime(date); - - if (params[0]->IsNumber()) { - WideString swResult; - switch (pRuntime->ToInt32(params[0])) { - case 0: - swResult.Format(L"D:%04d%02d%02d%02d%02d%02d", year, month, day, hour, - min, sec); - break; - case 1: - swResult.Format(L"%04d.%02d.%02d %02d:%02d:%02d", year, month, day, - hour, min, sec); - break; - case 2: - swResult.Format(L"%04d/%02d/%02d %02d:%02d:%02d", year, month, day, - hour, min, sec); - break; - default: - return CJS_Return(JSGetStringFromID(IDS_STRING_JSVALUEERROR)); - } - - return CJS_Return(pRuntime->NewString(swResult.c_str())); - } - - if (params[0]->IsString()) { - // We don't support XFAPicture at the moment. - if (iSize > 2 && pRuntime->ToBoolean(params[2])) - return CJS_Return(JSGetStringFromID(IDS_STRING_JSNOTSUPPORT)); - - // Convert PDF-style format specifiers to wcsftime specifiers. Remove any - // pre-existing %-directives before inserting our own. - std::basic_string cFormat = - pRuntime->ToWideString(params[0]).c_str(); - cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'), - cFormat.end()); - - for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) { - int iStart = 0; - int iEnd; - while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) != - -1) { - cFormat.replace(iEnd, wcslen(TbConvertTable[i].lpszJSMark), - TbConvertTable[i].lpszCppMark); - iStart = iEnd; - } - } - - if (year < 0) - return CJS_Return(JSGetStringFromID(IDS_STRING_JSVALUEERROR)); - - static const TbConvertAdditional cTableAd[] = { - {L"m", month}, {L"d", day}, - {L"H", hour}, {L"h", hour > 12 ? hour - 12 : hour}, - {L"M", min}, {L"s", sec}, - }; - - for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) { - WideString sValue; - sValue.Format(L"%d", cTableAd[i].iValue); - - int iStart = 0; - int iEnd; - while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) { - if (iEnd > 0) { - if (cFormat[iEnd - 1] == L'%') { - iStart = iEnd + 1; - continue; - } - } - cFormat.replace(iEnd, wcslen(cTableAd[i].lpszJSMark), sValue.c_str()); - iStart = iEnd; - } - } - - struct tm time = {}; - time.tm_year = year - 1900; - time.tm_mon = month - 1; - time.tm_mday = day; - time.tm_hour = hour; - time.tm_min = min; - time.tm_sec = sec; - - wchar_t buf[64] = {}; - FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time); - cFormat = buf; - return CJS_Return(pRuntime->NewString(cFormat.c_str())); - } - - return CJS_Return(JSGetStringFromID(IDS_STRING_JSTYPEERROR)); -} - -CJS_Return util::printx(CJS_Runtime* pRuntime, - const std::vector>& params) { - if (params.size() < 2) - return CJS_Return(JSGetStringFromID(IDS_STRING_JSPARAMERROR)); - - return CJS_Return( - pRuntime->NewString(printx(pRuntime->ToWideString(params[0]), - pRuntime->ToWideString(params[1])) - .c_str())); -} - -enum CaseMode { kPreserveCase, kUpperCase, kLowerCase }; - -static wchar_t TranslateCase(wchar_t input, CaseMode eMode) { - if (eMode == kLowerCase && FXSYS_isupper(input)) - return input | 0x20; - if (eMode == kUpperCase && FXSYS_islower(input)) - return input & ~0x20; - return input; -} - -WideString util::printx(const WideString& wsFormat, - const WideString& wsSource) { - WideString wsResult; - size_t iSourceIdx = 0; - size_t iFormatIdx = 0; - CaseMode eCaseMode = kPreserveCase; - bool bEscaped = false; - while (iFormatIdx < wsFormat.GetLength()) { - if (bEscaped) { - bEscaped = false; - wsResult += wsFormat[iFormatIdx]; - ++iFormatIdx; - continue; - } - switch (wsFormat[iFormatIdx]) { - case '\\': { - bEscaped = true; - ++iFormatIdx; - } break; - case '<': { - eCaseMode = kLowerCase; - ++iFormatIdx; - } break; - case '>': { - eCaseMode = kUpperCase; - ++iFormatIdx; - } break; - case '=': { - eCaseMode = kPreserveCase; - ++iFormatIdx; - } break; - case '?': { - if (iSourceIdx < wsSource.GetLength()) { - wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); - ++iSourceIdx; - } - ++iFormatIdx; - } break; - case 'X': { - if (iSourceIdx < wsSource.GetLength()) { - if (FXSYS_iswalnum(wsSource[iSourceIdx])) { - wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); - ++iFormatIdx; - } - ++iSourceIdx; - } else { - ++iFormatIdx; - } - } break; - case 'A': { - if (iSourceIdx < wsSource.GetLength()) { - if (FXSYS_iswalpha(wsSource[iSourceIdx])) { - wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); - ++iFormatIdx; - } - ++iSourceIdx; - } else { - ++iFormatIdx; - } - } break; - case '9': { - if (iSourceIdx < wsSource.GetLength()) { - if (std::iswdigit(wsSource[iSourceIdx])) { - wsResult += wsSource[iSourceIdx]; - ++iFormatIdx; - } - ++iSourceIdx; - } else { - ++iFormatIdx; - } - } break; - case '*': { - if (iSourceIdx < wsSource.GetLength()) { - wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); - ++iSourceIdx; - } else { - ++iFormatIdx; - } - } break; - default: { - wsResult += wsFormat[iFormatIdx]; - ++iFormatIdx; - } break; - } - } - return wsResult; -} - -CJS_Return util::scand(CJS_Runtime* pRuntime, - const std::vector>& params) { - if (params.size() < 2) - return CJS_Return(false); - - WideString sFormat = pRuntime->ToWideString(params[0]); - WideString sDate = pRuntime->ToWideString(params[1]); - double dDate = JS_GetDateTime(); - if (sDate.GetLength() > 0) - dDate = CJS_PublicMethods::MakeRegularDate(sDate, sFormat, nullptr); - - if (std::isnan(dDate)) - return CJS_Return(pRuntime->NewUndefined()); - return CJS_Return(pRuntime->NewDate(dDate)); -} - -CJS_Return util::byteToChar(CJS_Runtime* pRuntime, - const std::vector>& params) { - if (params.size() < 1) - return CJS_Return(JSGetStringFromID(IDS_STRING_JSPARAMERROR)); - - int arg = pRuntime->ToInt32(params[0]); - if (arg < 0 || arg > 255) - return CJS_Return(JSGetStringFromID(IDS_STRING_JSVALUEERROR)); - - WideString wStr(static_cast(arg)); - return CJS_Return(pRuntime->NewString(wStr.c_str())); -} - -// Ensure that sFormat contains at most one well-understood printf formatting -// directive which is safe to use with a single argument, and return the type -// of argument expected, or -1 otherwise. If -1 is returned, it is NOT safe -// to use sFormat with printf() and it must be copied byte-by-byte. -int util::ParseDataType(std::wstring* sFormat) { - enum State { BEFORE, FLAGS, WIDTH, PRECISION, SPECIFIER, AFTER }; - - int result = -1; - State state = BEFORE; - size_t precision_digits = 0; - size_t i = 0; - while (i < sFormat->length()) { - wchar_t c = (*sFormat)[i]; - switch (state) { - case BEFORE: - if (c == L'%') - state = FLAGS; - break; - case FLAGS: - if (c == L'+' || c == L'-' || c == L'#' || c == L' ') { - // Stay in same state. - } else { - state = WIDTH; - continue; // Re-process same character. - } - break; - case WIDTH: - if (c == L'*') - return -1; - if (std::iswdigit(c)) { - // Stay in same state. - } else if (c == L'.') { - state = PRECISION; - } else { - state = SPECIFIER; - continue; // Re-process same character. - } - break; - case PRECISION: - if (c == L'*') - return -1; - if (std::iswdigit(c)) { - // Stay in same state. - ++precision_digits; - } else { - state = SPECIFIER; - continue; // Re-process same character. - } - break; - case SPECIFIER: - if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' || - c == L'u' || c == L'x' || c == L'X') { - result = UTIL_INT; - } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' || - c == L'G') { - result = UTIL_DOUBLE; - } else if (c == L's' || c == L'S') { - // Map s to S since we always deal internally with wchar_t strings. - // TODO(tsepez): Probably 100% borked. %S is not a standard - // conversion. - (*sFormat)[i] = L'S'; - result = UTIL_STRING; - } else { - return -1; - } - state = AFTER; - break; - case AFTER: - if (c == L'%') - return -1; - // Stay in same state until string exhausted. - break; - } - ++i; - } - // See https://crbug.com/740166 - if (result == UTIL_INT && precision_digits > 2) - return -1; - - return result; -} diff --git a/fpdfsdk/javascript/util.h b/fpdfsdk/javascript/util.h deleted file mode 100644 index 269b89efa3..0000000000 --- a/fpdfsdk/javascript/util.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2014 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com - -#ifndef FPDFSDK_JAVASCRIPT_UTIL_H_ -#define FPDFSDK_JAVASCRIPT_UTIL_H_ - -#include -#include - -#include "fpdfsdk/javascript/JS_Define.h" - -// Return values for ParseDataType() below. -#define UTIL_INT 0 -#define UTIL_DOUBLE 1 -#define UTIL_STRING 2 - -class util : public CJS_EmbedObj { - public: - explicit util(CJS_Object* pJSObject); - ~util() override; - - CJS_Return printd(CJS_Runtime* pRuntime, - const std::vector>& params); - CJS_Return printf(CJS_Runtime* pRuntime, - const std::vector>& params); - CJS_Return printx(CJS_Runtime* pRuntime, - const std::vector>& params); - CJS_Return scand(CJS_Runtime* pRuntime, - const std::vector>& params); - CJS_Return byteToChar(CJS_Runtime* pRuntime, - const std::vector>& params); - - static WideString printx(const WideString& cFormat, - const WideString& cSource); - - private: - friend class CJS_Util_ParseDataType_Test; - - static int ParseDataType(std::wstring* sFormat); -}; - -class CJS_Util : public CJS_Object { - public: - static void DefineJSObjects(CFXJS_Engine* pEngine); - - explicit CJS_Util(v8::Local pObject) : CJS_Object(pObject) {} - ~CJS_Util() override {} - - JS_STATIC_METHOD(printd, util); - JS_STATIC_METHOD(printf, util); - JS_STATIC_METHOD(printx, util); - JS_STATIC_METHOD(scand, util); - JS_STATIC_METHOD(byteToChar, util); - - private: - static int ObjDefnID; - static const JSMethodSpec MethodSpecs[]; -}; - -#endif // FPDFSDK_JAVASCRIPT_UTIL_H_ diff --git a/fpdfsdk/javascript/util_unittest.cpp b/fpdfsdk/javascript/util_unittest.cpp deleted file mode 100644 index 8e28dc1bdc..0000000000 --- a/fpdfsdk/javascript/util_unittest.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2017 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "fpdfsdk/javascript/util.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/test_support.h" - -TEST(CJS_Util, ParseDataType) { - struct ParseDataTypeCase { - const wchar_t* const input_string; - const int expected; - }; - - // Commented out tests follow the spec but are not passing. - const ParseDataTypeCase cases[] = { - // Not conversions - {L"", -1}, - {L"d", -1}, - - // Simple cases - {L"%d", UTIL_INT}, - {L"%x", UTIL_INT}, - {L"%f", UTIL_DOUBLE}, - {L"%s", UTIL_STRING}, - - // nDecSep Not implemented - // {L"%,0d", UTIL_INT}, - // {L"%,1d", UTIL_INT}, - // {L"%,2d", UTIL_INT}, - // {L"%,3d", UTIL_INT}, - // {L"%,4d", -1}, - // {L"%,d", -1}, - - // cFlags("+ 0#"") are only valid for numeric conversions. - {L"%+d", UTIL_INT}, - {L"%+x", UTIL_INT}, - {L"%+f", UTIL_DOUBLE}, - // {L"%+s", -1}, - {L"% d", UTIL_INT}, - {L"% x", UTIL_INT}, - {L"% f", UTIL_DOUBLE}, - // {L"% s", -1}, - {L"%0d", UTIL_INT}, - {L"%0x", UTIL_INT}, - {L"%0f", UTIL_DOUBLE}, - // {L"%0s", -1}, - {L"%#d", UTIL_INT}, - {L"%#x", UTIL_INT}, - {L"%#f", UTIL_DOUBLE}, - // {L"%#s", -1}, - - // nWidth should work. for all conversions, can be combined with cFlags=0 - // for numbers. - {L"%5d", UTIL_INT}, - {L"%05d", UTIL_INT}, - {L"%5x", UTIL_INT}, - {L"%05x", UTIL_INT}, - {L"%5f", UTIL_DOUBLE}, - {L"%05f", UTIL_DOUBLE}, - {L"%5s", UTIL_STRING}, - // {L"%05s", -1}, - - // nPrecision should only work for float - // {L"%.5d", -1}, - // {L"%.5x", -1}, - {L"%.5f", UTIL_DOUBLE}, - // {L"%.5s", -1}, - // {L"%.14d", -1}, - // {L"%.14x", -1}, - {L"%.14f", UTIL_DOUBLE}, - // {L"%.14s", -1}, - // {L"%.f", -1}, - - // See https://crbug.com/740166 - // nPrecision too large (> 260) causes crashes in Windows. - // Avoid this by limiting to two digits - {L"%.1d", UTIL_INT}, - {L"%.10d", UTIL_INT}, - {L"%.100d", -1}, - - // Unexpected characters - {L"%ad", -1}, - {L"%bx", -1}, - // {L"%cf", -1}, - // {L"%es", -1}, - // {L"%gd", -1}, - {L"%hx", -1}, - // {L"%if", -1}, - {L"%js", -1}, - {L"%@d", -1}, - {L"%~x", -1}, - {L"%[f", -1}, - {L"%\0s", -1}, - {L"%\nd", -1}, - {L"%\rx", -1}, - // {L"%%f", -1}, - // {L"% s", -1}, - - // Combine multiple valid components - {L"%+6d", UTIL_INT}, - {L"% 7x", UTIL_INT}, - {L"%#9.3f", UTIL_DOUBLE}, - {L"%10s", UTIL_STRING}, - }; - - for (size_t i = 0; i < FX_ArraySize(cases); i++) { - std::wstring input(cases[i].input_string); - EXPECT_EQ(cases[i].expected, util::ParseDataType(&input)) - << cases[i].input_string; - } -} -- cgit v1.2.3