summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fpdfsdk/fpdfformfill_embeddertest.cpp22
-rw-r--r--fpdfsdk/javascript/JS_Object.cpp38
-rw-r--r--fpdfsdk/javascript/JS_Object.h10
-rw-r--r--fpdfsdk/javascript/app.cpp47
-rw-r--r--fpdfsdk/javascript/app.h6
-rw-r--r--testing/resources/bug_634716.in126
-rw-r--r--testing/resources/bug_634716.pdf180
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