From 86a61dc374e8abe351df03a1aa6665013cc39345 Mon Sep 17 00:00:00 2001 From: tsepez Date: Fri, 25 Mar 2016 10:00:11 -0700 Subject: util.printd() replaces specified date with current date. Added test case. Several bugs going on here: JS_LocalTime() ignoring argument and returning current time and not factoring in the time zone adjustment. Use of FXSYS_floor() silently casts result to float, losing precision required to extract minutes and seconds. Pre-existing wcsftime escapes not stripped. BUG=pdfium:413 Review URL: https://codereview.chromium.org/1833053002 --- DEPS | 2 +- fpdfsdk/javascript/JS_Value.cpp | 10 +- fpdfsdk/javascript/util.cpp | 221 ++++++--------------- fpdfsdk/javascript/util.h | 7 - testing/resources/javascript/util_printd.in | 104 ++++++++++ .../resources/javascript/util_printd_expected.txt | 40 ++++ 6 files changed, 216 insertions(+), 168 deletions(-) create mode 100644 testing/resources/javascript/util_printd.in create mode 100644 testing/resources/javascript/util_printd_expected.txt diff --git a/DEPS b/DEPS index b5899e7198..02561ad080 100644 --- a/DEPS +++ b/DEPS @@ -9,7 +9,7 @@ vars = { 'gmock_revision': '29763965ab52f24565299976b936d1265cb6a271', 'gtest_revision': '8245545b6dc9c4703e6496d1efd19e975ad2b038', 'icu_revision': '8d342a405be5ae8aacb1e16f0bc31c3a4fbf26a2', - 'pdfium_tests_revision': 'd671154d809b4fcf250e72523ef7d85df04c806a', + 'pdfium_tests_revision': 'eb87214cb2088536e96aae517f3a281818fbf5b0', 'skia_revision': '0a291c7b7eea1807bd58bdaa60c258fd0ebeb257', 'trace_event_revision': 'd83d44b13d07c2fd0a40101a7deef9b93b841732', 'v8_revision': '3c3d7e7be80f45eeea0dc74a71d7552e2afc2985', diff --git a/fpdfsdk/javascript/JS_Value.cpp b/fpdfsdk/javascript/JS_Value.cpp index 1da1525d09..5e403a524b 100644 --- a/fpdfsdk/javascript/JS_Value.cpp +++ b/fpdfsdk/javascript/JS_Value.cpp @@ -778,15 +778,15 @@ int JS_GetDayFromTime(double dt) { } int JS_GetHourFromTime(double dt) { - return (int)_Mod(FXSYS_floor((double)(dt / (60 * 60 * 1000))), 24); + return (int)_Mod(floor(dt / (60 * 60 * 1000)), 24); } int JS_GetMinFromTime(double dt) { - return (int)_Mod(FXSYS_floor((double)(dt / (60 * 1000))), 60); + return (int)_Mod(floor(dt / (60 * 1000)), 60); } int JS_GetSecFromTime(double dt) { - return (int)_Mod(FXSYS_floor((double)(dt / 1000)), 60); + return (int)_Mod(floor(dt / 1000), 60); } double JS_DateParse(const wchar_t* str) { @@ -820,7 +820,7 @@ double JS_DateParse(const wchar_t* str) { double date = v->ToNumber(context).ToLocalChecked()->Value(); if (!_isfinite(date)) return date; - return date + _getLocalTZA() + _getDaylightSavingTA(date); + return JS_LocalTime(date); } } } @@ -869,7 +869,7 @@ bool JS_PortIsNan(double d) { } double JS_LocalTime(double d) { - return JS_GetDateTime() + _getDaylightSavingTA(d); + return d + _getLocalTZA() + _getDaylightSavingTA(d); } std::vector JS_ExpandKeywordParams( diff --git a/fpdfsdk/javascript/util.cpp b/fpdfsdk/javascript/util.cpp index 5a7a895dac..30656cd766 100644 --- a/fpdfsdk/javascript/util.cpp +++ b/fpdfsdk/javascript/util.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -42,50 +43,39 @@ END_JS_STATIC_METHOD() IMPLEMENT_JS_CLASS(CJS_Util, util) -util::util(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {} +#define UTIL_INT 0 +#define UTIL_DOUBLE 1 +#define UTIL_STRING 2 -util::~util() {} +namespace { -struct stru_TbConvert { +// Map PDF-style directives to equivalent wcsftime directives. Not +// all have direct equivalents, though. +struct TbConvert { const FX_WCHAR* lpszJSMark; const FX_WCHAR* lpszCppMark; }; -const stru_TbConvert fcTable[] = { - {L"mmmm", L"%B"}, - {L"mmm", L"%b"}, - {L"mm", L"%m"}, - // "m" - {L"dddd", L"%A"}, - {L"ddd", L"%a"}, - {L"dd", L"%d"}, - // "d", "%w", - {L"yyyy", L"%Y"}, - {L"yy", L"%y"}, - {L"HH", L"%H"}, - // "H" - {L"hh", L"%I"}, - // "h" - {L"MM", L"%M"}, - // "M" - {L"ss", L"%S"}, - // "s +// Map PDF-style directives lacking direct wcsftime directives to +// the value with which they will be replaced. +struct TbConvertAdditional { + const FX_WCHAR* 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"}, -// "t" #if defined(_WIN32) - {L"tt", L"%p"}, - {L"h", L"%#I"}, + {L"tt", L"%p"}, {L"h", L"%#I"}, #else - {L"tt", L"%P"}, - {L"h", L"%l"}, + {L"tt", L"%P"}, {L"h", L"%l"}, #endif }; -#define UTIL_INT 0 -#define UTIL_DOUBLE 1 -#define UTIL_STRING 2 - -int util::ParstDataType(std::wstring* sFormat) { +int ParseDataType(std::wstring* sFormat) { bool bPercent = FALSE; for (size_t i = 0; i < sFormat->length(); ++i) { wchar_t c = (*sFormat)[i]; @@ -119,6 +109,12 @@ int util::ParstDataType(std::wstring* sFormat) { return -1; } +} // namespace + +util::util(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {} + +util::~util() {} + FX_BOOL util::printf(IJS_Context* cc, const std::vector& params, CJS_Value& vRet, @@ -143,8 +139,6 @@ FX_BOOL util::printf(IJS_Context* cc, } std::wstring c_strResult; - - // for(int iIndex = 1;iIndex < params.size();iIndex++) std::wstring c_strFormat; for (int iIndex = 0; iIndex < (int)c_strConvers.size(); iIndex++) { c_strFormat = c_strConvers[iIndex]; @@ -159,7 +153,7 @@ FX_BOOL util::printf(IJS_Context* cc, continue; } - switch (ParstDataType(&c_strFormat)) { + switch (ParseDataType(&c_strFormat)) { case UTIL_INT: strSegment.Format(c_strFormat.c_str(), params[iIndex].ToInt()); break; @@ -191,9 +185,7 @@ FX_BOOL util::printd(IJS_Context* cc, return FALSE; CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc); - CJS_Value p1(pRuntime); - p1 = params[0]; - + CJS_Value p1 = params[0]; CJS_Value p2 = params[1]; CJS_Date jsDate(pRuntime); if (!p2.ConvertToDate(jsDate)) { @@ -207,10 +199,8 @@ FX_BOOL util::printd(IJS_Context* cc, } if (p1.GetType() == CJS_Value::VT_number) { - int nFormat = p1.ToInt(); CFX_WideString swResult; - - switch (nFormat) { + switch (p1.ToInt()) { case 0: swResult.Format(L"D:%04d%02d%02d%02d%02d%02d", jsDate.GetYear(), jsDate.GetMonth() + 1, jsDate.GetDay(), @@ -230,63 +220,52 @@ FX_BOOL util::printd(IJS_Context* cc, jsDate.GetSeconds()); break; default: + sError = JSGetStringFromID((CJS_Context*)cc, IDS_STRING_JSVALUEERROR); return FALSE; } vRet = swResult.c_str(); return TRUE; } - if (p1.GetType() == CJS_Value::VT_string) { - std::basic_string cFormat = p1.ToCFXWideString().c_str(); - bool bXFAPicture = false; - if (iSize > 2) { - bXFAPicture = params[2].ToBool(); - } - - if (bXFAPicture) { + if (p1.GetType() == CJS_Value::VT_string) { + if (iSize > 2 && params[2].ToBool()) { + sError = JSGetStringFromID((CJS_Context*)cc, IDS_STRING_NOTSUPPORT); return FALSE; // currently, it doesn't support XFAPicture. } - for (size_t i = 0; i < sizeof(fcTable) / sizeof(stru_TbConvert); ++i) { + // Convert PDF-style format specifiers to wcsftime specifiers. Remove any + // pre-existing %-directives before inserting our own. + std::basic_string cFormat = p1.ToCFXWideString().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(fcTable[i].lpszJSMark, iStart)) != -1) { - cFormat.replace(iEnd, FXSYS_wcslen(fcTable[i].lpszJSMark), - fcTable[i].lpszCppMark); + while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) != + -1) { + cFormat.replace(iEnd, FXSYS_wcslen(TbConvertTable[i].lpszJSMark), + TbConvertTable[i].lpszCppMark); iStart = iEnd; } } - int iYear, iMonth, iDay, iHour, iMin, iSec; - iYear = jsDate.GetYear(); - iMonth = jsDate.GetMonth(); - iDay = jsDate.GetDay(); - iHour = jsDate.GetHours(); - iMin = jsDate.GetMinutes(); - iSec = jsDate.GetSeconds(); - - struct tm time = {}; - time.tm_year = iYear - 1900; - time.tm_mon = iMonth; - time.tm_mday = iDay; - time.tm_hour = iHour; - time.tm_min = iMin; - time.tm_sec = iSec; - - struct stru_TbConvertAd { - const FX_WCHAR* lpszJSMark; - int iValue; - }; + int iYear = jsDate.GetYear(); + int iMonth = jsDate.GetMonth(); + int iDay = jsDate.GetDay(); + int iHour = jsDate.GetHours(); + int iMin = jsDate.GetMinutes(); + int iSec = jsDate.GetSeconds(); - stru_TbConvertAd cTableAd[] = { + TbConvertAdditional cTableAd[] = { {L"m", iMonth + 1}, {L"d", iDay}, {L"H", iHour}, {L"h", iHour > 12 ? iHour - 12 : iHour}, {L"M", iMin}, {L"s", iSec}, }; - for (size_t i = 0; i < sizeof(cTableAd) / sizeof(stru_TbConvertAd); ++i) { - wchar_t tszValue[10]; + for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) { + wchar_t tszValue[16]; CFX_WideString sValue; sValue.Format(L"%d", cTableAd[i].iValue); memcpy(tszValue, (wchar_t*)sValue.GetBuffer(sValue.GetLength() + 1), @@ -306,93 +285,25 @@ FX_BOOL util::printd(IJS_Context* cc, } } - CFX_WideString strFormat; + struct tm time = {}; + time.tm_year = iYear - 1900; + time.tm_mon = iMonth; + time.tm_mday = iDay; + time.tm_hour = iHour; + time.tm_min = iMin; + time.tm_sec = iSec; + wchar_t buf[64] = {}; - strFormat = wcsftime(buf, 64, cFormat.c_str(), &time); + wcsftime(buf, 64, cFormat.c_str(), &time); cFormat = buf; vRet = cFormat.c_str(); return TRUE; } + + sError = JSGetStringFromID((CJS_Context*)cc, IDS_STRING_JSTYPEERROR); return FALSE; } -void util::printd(const std::wstring& cFormat2, - CJS_Date jsDate, - bool bXFAPicture, - std::wstring& cPurpose) { - std::wstring cFormat = cFormat2; - - if (bXFAPicture) { - return; // currently, it doesn't support XFAPicture. - } - - for (size_t i = 0; i < sizeof(fcTable) / sizeof(stru_TbConvert); ++i) { - int iStart = 0; - int iEnd; - while ((iEnd = cFormat.find(fcTable[i].lpszJSMark, iStart)) != -1) { - cFormat.replace(iEnd, FXSYS_wcslen(fcTable[i].lpszJSMark), - fcTable[i].lpszCppMark); - iStart = iEnd; - } - } - - int iYear, iMonth, iDay, iHour, iMin, iSec; - iYear = jsDate.GetYear(); - iMonth = jsDate.GetMonth(); - iDay = jsDate.GetDay(); - iHour = jsDate.GetHours(); - iMin = jsDate.GetMinutes(); - iSec = jsDate.GetSeconds(); - - struct tm time = {}; - time.tm_year = iYear - 1900; - time.tm_mon = iMonth; - time.tm_mday = iDay; - time.tm_hour = iHour; - time.tm_min = iMin; - time.tm_sec = iSec; - // COleDateTime cppTm(iYear,iMonth+1,iDay,iHour,iMin,iSec); - // CString strFormat = cppTm.Format(cFormat.c_str()); - - struct stru_TbConvertAd { - const FX_WCHAR* lpszJSMark; - int iValue; - }; - - stru_TbConvertAd cTableAd[] = { - {L"m", iMonth + 1}, {L"d", iDay}, - {L"H", iHour}, {L"h", iHour > 12 ? iHour - 12 : iHour}, - {L"M", iMin}, {L"s", iSec}, - }; - - // cFormat = strFormat.GetBuffer(strFormat.GetLength()+1); - for (size_t i = 0; i < sizeof(cTableAd) / sizeof(stru_TbConvertAd); ++i) { - wchar_t tszValue[10]; - CFX_WideString sValue; - sValue.Format(L"%d", cTableAd[i].iValue); - memcpy(tszValue, (wchar_t*)sValue.GetBuffer(sValue.GetLength() + 1), - sValue.GetLength() * sizeof(wchar_t)); - - 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, FXSYS_wcslen(cTableAd[i].lpszJSMark), tszValue); - iStart = iEnd; - } - } - - CFX_WideString strFormat; - wchar_t buf[64] = {}; - strFormat = wcsftime(buf, 64, cFormat.c_str(), &time); - cFormat = buf; - cPurpose = cFormat; -} FX_BOOL util::printx(IJS_Context* cc, const std::vector& params, diff --git a/fpdfsdk/javascript/util.h b/fpdfsdk/javascript/util.h index 50fa31ad27..17f252d6dc 100644 --- a/fpdfsdk/javascript/util.h +++ b/fpdfsdk/javascript/util.h @@ -17,7 +17,6 @@ class util : public CJS_EmbedObj { util(CJS_Object* pJSObject); ~util() override; - public: FX_BOOL printd(IJS_Context* cc, const std::vector& params, CJS_Value& vRet, @@ -39,15 +38,9 @@ class util : public CJS_EmbedObj { CJS_Value& vRet, CFX_WideString& sError); - public: - static void printd(const std::wstring& cFormat, - CJS_Date Date, - bool bXFAPicture, - std::wstring& cPurpose); static void printx(const std::string& cFormat, const std::string& cSource, std::string& cPurpose); - static int ParstDataType(std::wstring* sFormat); }; class CJS_Util : public CJS_Object { diff --git a/testing/resources/javascript/util_printd.in b/testing/resources/javascript/util_printd.in new file mode 100644 index 0000000000..f098187cf0 --- /dev/null +++ b/testing/resources/javascript/util_printd.in @@ -0,0 +1,104 @@ +{{header}} +{{object 1 0}} << + /Type /Catalog + /Pages 2 0 R + /OpenAction 10 0 R +>> +endobj +{{object 2 0}} << + /Type /Pages + /Count 1 + /Kids [ + 3 0 R + ] +>> +endobj +% Page number 0. +{{object 3 0}} << + /Type /Page + /Parent 2 0 R + /Resources << + /Font <> + >> + /Contents [21 0 R] + /MediaBox [0 0 612 792] +>> +% OpenAction action +{{object 10 0}} << + /Type /Action + /S /JavaScript + /JS 11 0 R +>> +endobj +% JS program to exexute +{{object 11 0}} << +>> +stream +function TestOneFormat(str, d) { + try { + app.alert(str + ": " + util.printd(str, d)); + } + catch (e) { + app.alert(str + ": Caught error: " + e); + } +} +function TestOneXFAFormat(str, d, flag) { + try { + app.alert(str + ": " + util.printd(str, d, flag)); + } + catch (e) { + app.alert(str + ": Caught error: " + e); + } +} +// July 4th, 2014 11:59:59 AM local time. +var d1 = new Date(2014, 06, 04, 15, 59, 58); +TestOneFormat("mm/dd/yyyy HH:MM:ss", d1); +TestOneFormat(0, d1); +TestOneFormat(1, d1); +TestOneFormat(2, d1); +TestOneFormat(3, d1); +TestOneFormat("mmmm", d1); +TestOneFormat("mmm", d1); +TestOneFormat("mm", d1); +TestOneFormat("m", d1); +TestOneFormat("dddd", d1); +TestOneFormat("ddd", d1); +TestOneFormat("dd", d1); +TestOneFormat("d", d1); +TestOneFormat("yyyy", d1); +TestOneFormat("yy", d1); +TestOneFormat("HH", d1); +TestOneFormat("H", d1); +TestOneFormat("hh", d1); +// "h" is inconsitent between platforms: " 3" vs. "3" +TestOneFormat("MM", d1); +TestOneFormat("M", d1); +TestOneFormat("ss", d1); +TestOneFormat("s", d1); +// "tt" is inconsitent between platforms: "PM" vs, "pm" vs. "P" +TestOneFormat("t", d1); +TestOneFormat("abc.efg.i.kl.nopqr..uvwxyzABC.EFG.I.KL.NOPQR..UVWXYZ0123456780", d1); +TestOneFormat("!@#$^&*()-_<>[];:~", d1); +TestOneFormat("%z %d %%z %%d %%%z %%%d %%% hh:MM", d1); +TestOneFormat("", d1); +TestOneFormat("mm/dd/yyyy", d1); +TestOneFormat("mm/dd/yyyy", new Date(1850, 0, 1)); +TestOneFormat("mm/dd/yyyy", new Date(2525, 11, 31)); +TestOneFormat("mm/dd/yyyy"); +TestOneFormat(); +TestOneFormat("mm/dd/yyyy", 42); +TestOneFormat("mm/dd/yyyy", "clams"); +TestOneFormat("mm/dd/yyyy", {"clams": 3}); +TestOneFormat("mm/dd/yyyy", ["clams", 3]); +TestOneFormat({"clams": 3}, d1); +TestOneFormat(["clams", 3], d1); +TestOneXFAFormat("mm", d1, false); +TestOneXFAFormat("mm", d1, true); +endstream +endobj +{{xref}} +trailer << + /Root 1 0 R +>> +{{startxref}} +%%EOF diff --git a/testing/resources/javascript/util_printd_expected.txt b/testing/resources/javascript/util_printd_expected.txt new file mode 100644 index 0000000000..47abb2fd42 --- /dev/null +++ b/testing/resources/javascript/util_printd_expected.txt @@ -0,0 +1,40 @@ +Alert: mm/dd/yyyy HH:MM:ss: 07/04/2014 15:59:58 +Alert: 0: D:20140704155958 +Alert: 1: 2014.07.04 15:59:58 +Alert: 2: 2014/07/04 15:59:58 +Alert: 3: Caught error: util.printd: Incorrect parameter value. +Alert: mmmm: July +Alert: mmm: Jul +Alert: mm: 07 +Alert: m: 7 +Alert: dddd: Sunday +Alert: ddd: Sun +Alert: dd: 04 +Alert: d: 4 +Alert: yyyy: 2014 +Alert: yy: 14 +Alert: HH: 15 +Alert: H: 15 +Alert: hh: 03 +Alert: MM: 59 +Alert: M: 59 +Alert: ss: 58 +Alert: s: 58 +Alert: t: t +Alert: abc.efg.i.kl.nopqr..uvwxyzABC.EFG.I.KL.NOPQR..UVWXYZ0123456780: abc.efg.i.kl.nopqr..uvwxyzABC.EFG.I.KL.NOPQR..UVWXYZ0123456780 +Alert: !@#$^&*()-_<>[];:~: !@#$^&*()-_<>[];:~ +Alert: %z %d %%z %%d %%%z %%%d %%% hh:MM: z 4 z 4 z 4 03:59 +Alert: : +Alert: mm/dd/yyyy: 07/04/2014 +Alert: mm/dd/yyyy: 01/01/1850 +Alert: mm/dd/yyyy: 12/31/2525 +Alert: mm/dd/yyyy: Caught error: util.printd: The second parameter can't be converted to a Date. +Alert: undefined: Caught error: util.printd: The second parameter can't be converted to a Date. +Alert: mm/dd/yyyy: Caught error: util.printd: The second parameter can't be converted to a Date. +Alert: mm/dd/yyyy: Caught error: util.printd: The second parameter can't be converted to a Date. +Alert: mm/dd/yyyy: Caught error: util.printd: The second parameter can't be converted to a Date. +Alert: mm/dd/yyyy: Caught error: util.printd: The second parameter can't be converted to a Date. +Alert: [object Object]: Caught error: util.printd: Incorrect parameter type. +Alert: clams,3: Caught error: util.printd: Incorrect parameter type. +Alert: mm: 07 +Alert: mm: Caught error: util.printd: Operation not supported. -- cgit v1.2.3