diff options
-rw-r--r-- | fpdfsdk/fpdfformfill_embeddertest.cpp | 22 | ||||
-rw-r--r-- | fpdfsdk/javascript/JS_Object.cpp | 38 | ||||
-rw-r--r-- | fpdfsdk/javascript/JS_Object.h | 10 | ||||
-rw-r--r-- | fpdfsdk/javascript/app.cpp | 47 | ||||
-rw-r--r-- | fpdfsdk/javascript/app.h | 6 | ||||
-rw-r--r-- | testing/resources/bug_634716.in | 126 | ||||
-rw-r--r-- | testing/resources/bug_634716.pdf | 180 |
7 files changed, 374 insertions, 55 deletions
diff --git a/fpdfsdk/fpdfformfill_embeddertest.cpp b/fpdfsdk/fpdfformfill_embeddertest.cpp index a1425153a1..3a6a7448e5 100644 --- a/fpdfsdk/fpdfformfill_embeddertest.cpp +++ b/fpdfsdk/fpdfformfill_embeddertest.cpp @@ -157,4 +157,26 @@ TEST_F(FPDFFormFillEmbeddertest, BUG_634394) { EXPECT_EQ(2U, alerts.size()); } +TEST_F(FPDFFormFillEmbeddertest, BUG_634716) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + + EXPECT_TRUE(OpenDocument("bug_634716.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + DoOpenActions(); + + // Timers fire at most once per AdvanceTime(), allow intervals + // to fire several times if possible. + delegate.AdvanceTime(1000); + delegate.AdvanceTime(1000); + delegate.AdvanceTime(1000); + delegate.AdvanceTime(1000); + delegate.AdvanceTime(1000); + UnloadPage(page); + + const auto& alerts = delegate.GetAlerts(); + EXPECT_EQ(2U, alerts.size()); +} + #endif // PDF_ENABLE_V8 diff --git a/fpdfsdk/javascript/JS_Object.cpp b/fpdfsdk/javascript/JS_Object.cpp index 9ec316303d..b65699a18b 100644 --- a/fpdfsdk/javascript/JS_Object.cpp +++ b/fpdfsdk/javascript/JS_Object.cpp @@ -90,7 +90,7 @@ CJS_Timer::CJS_Timer(CJS_EmbedObj* pObj, m_pRuntime(pRuntime), m_pApp(pApp) { CFX_SystemHandler* pHandler = m_pApp->GetSysHandler(); - m_nTimerID = pHandler->SetTimer(dwElapse, TimerProc); + m_nTimerID = pHandler->SetTimer(dwElapse, Trigger); (*GetGlobalTimerMap())[m_nTimerID] = this; m_pRuntime->AddObserver(this); } @@ -99,23 +99,19 @@ CJS_Timer::~CJS_Timer() { CJS_Runtime* pRuntime = GetRuntime(); if (pRuntime) pRuntime->RemoveObserver(this); - KillJSTimer(); -} -void CJS_Timer::KillJSTimer() { - if (m_nTimerID) { - if (m_bValid) { - CFX_SystemHandler* pHandler = m_pApp->GetSysHandler(); - pHandler->KillTimer(m_nTimerID); - } - GetGlobalTimerMap()->erase(m_nTimerID); - m_nTimerID = 0; - } + if (!m_nTimerID) + return; + + if (m_bValid) + m_pApp->GetSysHandler()->KillTimer(m_nTimerID); + + GetGlobalTimerMap()->erase(m_nTimerID); } // static -void CJS_Timer::TimerProc(int idEvent) { - auto it = GetGlobalTimerMap()->find(idEvent); +void CJS_Timer::Trigger(int nTimerID) { + auto it = GetGlobalTimerMap()->find(nTimerID); if (it == GetGlobalTimerMap()->end()) return; @@ -128,12 +124,24 @@ void CJS_Timer::TimerProc(int idEvent) { pTimer->m_pEmbedObj->TimerProc(pTimer); // Timer proc may have destroyed timer, find it again. - it = GetGlobalTimerMap()->find(idEvent); + it = GetGlobalTimerMap()->find(nTimerID); if (it == GetGlobalTimerMap()->end()) return; pTimer = it->second; pTimer->m_bProcessing = false; + if (pTimer->IsOneShot()) + pTimer->m_pEmbedObj->CancelProc(pTimer); +} + +// static +void CJS_Timer::Cancel(int nTimerID) { + auto it = GetGlobalTimerMap()->find(nTimerID); + if (it == GetGlobalTimerMap()->end()) + return; + + CJS_Timer* pTimer = it->second; + pTimer->m_pEmbedObj->CancelProc(pTimer); } // static diff --git a/fpdfsdk/javascript/JS_Object.h b/fpdfsdk/javascript/JS_Object.h index 146a7c93b5..aec94c0362 100644 --- a/fpdfsdk/javascript/JS_Object.h +++ b/fpdfsdk/javascript/JS_Object.h @@ -18,12 +18,14 @@ class CJS_Context; class CJS_Object; class CJS_Timer; class CPDFDoc_Environment; + class CJS_EmbedObj { public: explicit CJS_EmbedObj(CJS_Object* pJSObject); virtual ~CJS_EmbedObj(); virtual void TimerProc(CJS_Timer* pTimer) {} + virtual void CancelProc(CJS_Timer* pTimer) {} CJS_Object* GetJSObject() const { return m_pJSObject; } @@ -76,15 +78,15 @@ class CJS_Timer : public CJS_Runtime::Observer { uint32_t dwTimeOut); ~CJS_Timer() override; - void KillJSTimer(); + static void Trigger(int nTimerID); + static void Cancel(int nTimerID); - int GetType() const { return m_nType; } + bool IsOneShot() const { return m_nType == 1; } uint32_t GetTimeOut() const { return m_dwTimeOut; } + int GetTimerID() const { return m_nTimerID; } CJS_Runtime* GetRuntime() const { return m_bValid ? m_pRuntime : nullptr; } CFX_WideString GetJScript() const { return m_swJScript; } - static void TimerProc(int idEvent); - private: using TimerMap = std::map<FX_UINT, CJS_Timer*>; static TimerMap* GetGlobalTimerMap(); diff --git a/fpdfsdk/javascript/app.cpp b/fpdfsdk/javascript/app.cpp index 1077629244..329b625545 100644 --- a/fpdfsdk/javascript/app.cpp +++ b/fpdfsdk/javascript/app.cpp @@ -31,16 +31,12 @@ END_JS_STATIC_METHOD() IMPLEMENT_JS_CLASS(CJS_TimerObj, TimerObj) TimerObj::TimerObj(CJS_Object* pJSObject) - : CJS_EmbedObj(pJSObject), m_pTimer(nullptr) {} + : CJS_EmbedObj(pJSObject), m_nTimerID(0) {} TimerObj::~TimerObj() {} void TimerObj::SetTimer(CJS_Timer* pTimer) { - m_pTimer = pTimer; -} - -CJS_Timer* TimerObj::GetTimer() const { - return m_pTimer; + m_nTimerID = pTimer->GetTimerID(); } #define JS_STR_VIEWERTYPE L"pdfium" @@ -473,20 +469,7 @@ void app::ClearTimerCommon(const CJS_Value& param) { if (!pTimerObj) return; - CJS_Timer* pTimer = pTimerObj->GetTimer(); - if (!pTimer) - return; - - pTimer->KillJSTimer(); - auto iter = std::find_if(m_Timers.begin(), m_Timers.end(), - [pTimer](const std::unique_ptr<CJS_Timer>& that) { - return pTimer == that.get(); - }); - - if (iter != m_Timers.end()) - m_Timers.erase(iter); - - pTimerObj->SetTimer(nullptr); + CJS_Timer::Cancel(pTimerObj->GetTimerID()); } FX_BOOL app::execMenuItem(IJS_Context* cc, @@ -498,20 +481,18 @@ FX_BOOL app::execMenuItem(IJS_Context* cc, void app::TimerProc(CJS_Timer* pTimer) { CJS_Runtime* pRuntime = pTimer->GetRuntime(); + if (pRuntime && (!pTimer->IsOneShot() || pTimer->GetTimeOut() > 0)) + RunJsScript(pRuntime, pTimer->GetJScript()); +} - switch (pTimer->GetType()) { - case 0: // interval - if (pRuntime) - RunJsScript(pRuntime, pTimer->GetJScript()); - break; - case 1: - if (pTimer->GetTimeOut() > 0) { - if (pRuntime) - RunJsScript(pRuntime, pTimer->GetJScript()); - pTimer->KillJSTimer(); - } - break; - } +void app::CancelProc(CJS_Timer* pTimer) { + auto iter = std::find_if(m_Timers.begin(), m_Timers.end(), + [pTimer](const std::unique_ptr<CJS_Timer>& that) { + return pTimer == that.get(); + }); + + if (iter != m_Timers.end()) + m_Timers.erase(iter); } void app::RunJsScript(CJS_Runtime* pRuntime, const CFX_WideString& wsScript) { diff --git a/fpdfsdk/javascript/app.h b/fpdfsdk/javascript/app.h index c6cda555d9..bb083cedb4 100644 --- a/fpdfsdk/javascript/app.h +++ b/fpdfsdk/javascript/app.h @@ -20,12 +20,11 @@ class TimerObj : public CJS_EmbedObj { TimerObj(CJS_Object* pJSObject); ~TimerObj() override; - public: void SetTimer(CJS_Timer* pTimer); - CJS_Timer* GetTimer() const; + int GetTimerID() const { return m_nTimerID; } private: - CJS_Timer* m_pTimer; + int m_nTimerID; // Weak reference to timer through global map. }; class CJS_TimerObj : public CJS_Object { @@ -158,6 +157,7 @@ class app : public CJS_EmbedObj { private: // CJS_EmbedObj void TimerProc(CJS_Timer* pTimer) override; + void CancelProc(CJS_Timer* pTimer) override; void RunJsScript(CJS_Runtime* pRuntime, const CFX_WideString& wsScript); void ClearTimerCommon(const CJS_Value& param); diff --git a/testing/resources/bug_634716.in b/testing/resources/bug_634716.in new file mode 100644 index 0000000000..a485daed97 --- /dev/null +++ b/testing/resources/bug_634716.in @@ -0,0 +1,126 @@ +{{header}} +{{object 1 0}} << + /Type /Catalog + /Pages 2 0 R + /AcroForm 6 0 R + /Names <</JavaScript 13 0 R>> +>> +endobj +{{object 2 0}} << + /Type /Pages + /Count 1 + /Kids [4 0 R] +>> +endobj +{{object 4 0}} << + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 612 792] + /CropBox [0 0 612 792] + /Resources <<>> +>> +endobj +{{object 6 0}} << + /DR << + /Font <</Helv 7 0 R>> + >> + /DA (/Helv 0 Tf 0 g) + /Fields [5 0 R] +>> +endobj +{{object 7 0}} << + /Type /Font + /Subtype /Type1 + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding +>> +endobj +{{object 8 0}} << + /Type /XObject + /Subtype /Form + /FormType 1 + /Matrix [1 0 0 1 0 0] + /BBox [0 0 75.907 28.472] + /Resources << + /Font <</FXF0 7 0 R>> + >> +>> +stream +q +Q + + +endstream +endobj +{{object 11 0}} << + /Type /Action + /S /JavaScript + /JS 50 0 R +>> +endobj +{{object 13 0}} << + /Names [(startDelay) 11 0 R] +>> +endobj +{{object 50 0}} << +>> +stream +var timeOut = 0; +var array = new Array(1024*4); +var doc = this; + +function myFunction() { + try { + // Free the Timer Objects + app.clearTimeOut(timeOut); + for (var i=0; i<array.length; i++) { + for (var j=0; j<array[i].length; j++) { + app.clearTimeOut(array[i][j]); + } + } + + // Trigger the Garbage Collection + array.length = 0; + array.push(new ArrayBuffer(1024*1024)); + array.length=0; + array.length=1024*4; + var str = 'AA'; + for (var i = 0; i < array.length ; i++) { + for (var j = 0; j < 10; j++ ) { + doc.addIcon(str + "-" + str + str + str + str + str, doc.icons); + } + } + } catch(err) { + app.alert(err); + } +} + +function main() { + try { + for (var i = 0; i < array.length ; i++) { + if (i == array.length / 2) { + timeOut = app.setTimeOut("myFunction()", 1000); + } + array[i] = new Array(4); + for (var j = 0; j < array[i].length ; j++ ) { + array[i][j] = app.setTimeOut("aaaaaaaaaa()", 100000); + } + } + } catch(err) { + app.alert(err); + } +} + +// Execute the JS. +app.setTimeOut("main()", 1000); + +// Be sure all of this code ran to completion. +app.alert("done"); +endstream +endobj +{{xref}} +trailer << + /Root 1 0 R +>> +{{startxref}} +%%EOF diff --git a/testing/resources/bug_634716.pdf b/testing/resources/bug_634716.pdf new file mode 100644 index 0000000000..6330b9a4e5 --- /dev/null +++ b/testing/resources/bug_634716.pdf @@ -0,0 +1,180 @@ +%PDF-1.7 +% ò¤ô +1 0 obj << + /Type /Catalog + /Pages 2 0 R + /AcroForm 6 0 R + /Names <</JavaScript 13 0 R>> +>> +endobj +2 0 obj << + /Type /Pages + /Count 1 + /Kids [4 0 R] +>> +endobj +4 0 obj << + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 612 792] + /CropBox [0 0 612 792] + /Resources <<>> +>> +endobj +6 0 obj << + /DR << + /Font <</Helv 7 0 R>> + >> + /DA (/Helv 0 Tf 0 g) + /Fields [5 0 R] +>> +endobj +7 0 obj << + /Type /Font + /Subtype /Type1 + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding +>> +endobj +8 0 obj << + /Type /XObject + /Subtype /Form + /FormType 1 + /Matrix [1 0 0 1 0 0] + /BBox [0 0 75.907 28.472] + /Resources << + /Font <</FXF0 7 0 R>> + >> +>> +stream +q +Q + + +endstream +endobj +11 0 obj << + /Type /Action + /S /JavaScript + /JS 50 0 R +>> +endobj +13 0 obj << + /Names [(startDelay) 11 0 R] +>> +endobj +50 0 obj << +>> +stream +var timeOut = 0; +var array = new Array(1024*4); +var doc = this; + +function myFunction() { + try { + // Free the Timer Objects + app.clearTimeOut(timeOut); + for (var i=0; i<array.length; i++) { + for (var j=0; j<array[i].length; j++) { + app.clearTimeOut(array[i][j]); + } + } + + // Trigger the Garbage Collection + array.length = 0; + array.push(new ArrayBuffer(1024*1024)); + array.length=0; + array.length=1024*4; + var str = 'AA'; + for (var i = 0; i < array.length ; i++) { + for (var j = 0; j < 10; j++ ) { + doc.addIcon(str + "-" + str + str + str + str + str, doc.icons); + } + } + } catch(err) { + app.alert(err); + } +} + +function main() { + try { + for (var i = 0; i < array.length ; i++) { + if (i == array.length / 2) { + timeOut = app.setTimeOut("myFunction()", 1000); + } + array[i] = new Array(4); + for (var j = 0; j < array[i].length ; j++ ) { + array[i][j] = app.setTimeOut("aaaaaaaaaa()", 100000); + } + } + } catch(err) { + app.alert(err); + } +} + +// Execute the JS. +app.setTimeOut("main()", 1000); + +// Be sure all of this code ran to completion. +app.alert("done"); +endstream +endobj +xref +0 51 +0000000000 65535 f +0000000015 00000 n +0000000118 00000 n +0000000000 65535 f +0000000181 00000 n +0000000000 65535 f +0000000302 00000 n +0000000404 00000 n +0000000509 00000 n +0000000000 65535 f +0000000000 65535 f +0000000701 00000 n +0000000000 65535 f +0000000769 00000 n +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000000 65535 f +0000000822 00000 n +trailer << + /Root 1 0 R +>> +startxref +2036 +%%EOF |