Index: fpdfsdk/javascript/app.cpp |
diff --git a/fpdfsdk/javascript/app.cpp b/fpdfsdk/javascript/app.cpp |
index 673852039057ce2414fa293f4182817b41986cba..755652d219ec35d79e0ff39305ac388a497ea2e2 100644 |
--- a/fpdfsdk/javascript/app.cpp |
+++ b/fpdfsdk/javascript/app.cpp |
@@ -19,6 +19,128 @@ |
#include "fpdfsdk/javascript/cjs_runtime.h" |
#include "fpdfsdk/javascript/resource.h" |
+class GlobalTimer : public CJS_Runtime::Observer { |
+ public: |
+ GlobalTimer(app* pObj, |
+ CPDFDoc_Environment* pApp, |
+ CJS_Runtime* pRuntime, |
+ int nType, |
+ const CFX_WideString& script, |
+ uint32_t dwElapse, |
+ uint32_t dwTimeOut); |
+ ~GlobalTimer() override; |
+ |
+ static void Trigger(int nTimerID); |
+ static void Cancel(int nTimerID); |
+ |
+ 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; } |
+ |
+ private: |
+ using TimerMap = std::map<FX_UINT, GlobalTimer*>; |
+ static TimerMap* GetGlobalTimerMap(); |
+ |
+ // CJS_Runtime::Observer |
+ void OnDestroyed() override; |
+ |
+ uint32_t m_nTimerID; |
+ app* const m_pEmbedObj; |
+ bool m_bProcessing; |
+ bool m_bValid; |
+ |
+ // data |
+ const int m_nType; // 0:Interval; 1:TimeOut |
+ const uint32_t m_dwTimeOut; |
+ const CFX_WideString m_swJScript; |
+ CJS_Runtime* const m_pRuntime; |
+ CPDFDoc_Environment* const m_pApp; |
+}; |
+ |
+GlobalTimer::GlobalTimer(app* pObj, |
+ CPDFDoc_Environment* pApp, |
+ CJS_Runtime* pRuntime, |
+ int nType, |
+ const CFX_WideString& script, |
+ uint32_t dwElapse, |
+ uint32_t dwTimeOut) |
+ : m_nTimerID(0), |
+ m_pEmbedObj(pObj), |
+ m_bProcessing(false), |
+ m_bValid(true), |
+ m_nType(nType), |
+ m_dwTimeOut(dwTimeOut), |
+ m_swJScript(script), |
+ m_pRuntime(pRuntime), |
+ m_pApp(pApp) { |
+ CFX_SystemHandler* pHandler = m_pApp->GetSysHandler(); |
+ m_nTimerID = pHandler->SetTimer(dwElapse, Trigger); |
+ (*GetGlobalTimerMap())[m_nTimerID] = this; |
+ m_pRuntime->AddObserver(this); |
+} |
+ |
+GlobalTimer::~GlobalTimer() { |
+ CJS_Runtime* pRuntime = GetRuntime(); |
+ if (pRuntime) |
+ pRuntime->RemoveObserver(this); |
+ |
+ if (!m_nTimerID) |
+ return; |
+ |
+ if (m_bValid) |
+ m_pApp->GetSysHandler()->KillTimer(m_nTimerID); |
+ |
+ GetGlobalTimerMap()->erase(m_nTimerID); |
+} |
+ |
+// static |
+void GlobalTimer::Trigger(int nTimerID) { |
+ auto it = GetGlobalTimerMap()->find(nTimerID); |
+ if (it == GetGlobalTimerMap()->end()) |
+ return; |
+ |
+ GlobalTimer* pTimer = it->second; |
+ if (pTimer->m_bProcessing) |
+ return; |
+ |
+ pTimer->m_bProcessing = true; |
+ if (pTimer->m_pEmbedObj) |
+ pTimer->m_pEmbedObj->TimerProc(pTimer); |
+ |
+ // Timer proc may have destroyed timer, find it again. |
+ 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 GlobalTimer::Cancel(int nTimerID) { |
+ auto it = GetGlobalTimerMap()->find(nTimerID); |
+ if (it == GetGlobalTimerMap()->end()) |
+ return; |
+ |
+ GlobalTimer* pTimer = it->second; |
+ pTimer->m_pEmbedObj->CancelProc(pTimer); |
+} |
+ |
+// static |
+GlobalTimer::TimerMap* GlobalTimer::GetGlobalTimerMap() { |
+ // Leak the timer array at shutdown. |
+ static auto* s_TimerMap = new TimerMap; |
+ return s_TimerMap; |
+} |
+ |
+void GlobalTimer::OnDestroyed() { |
+ m_bValid = false; |
+} |
+ |
BEGIN_JS_STATIC_CONST(CJS_TimerObj) |
END_JS_STATIC_CONST() |
@@ -35,7 +157,7 @@ TimerObj::TimerObj(CJS_Object* pJSObject) |
TimerObj::~TimerObj() {} |
-void TimerObj::SetTimer(CJS_Timer* pTimer) { |
+void TimerObj::SetTimer(GlobalTimer* pTimer) { |
m_nTimerID = pTimer->GetTimerID(); |
} |
@@ -375,8 +497,8 @@ FX_BOOL app::setInterval(IJS_Context* cc, |
CJS_Runtime* pRuntime = pContext->GetJSRuntime(); |
uint32_t dwInterval = params.size() > 1 ? params[1].ToInt() : 1000; |
CPDFDoc_Environment* pApp = pRuntime->GetReaderApp(); |
- m_Timers.push_back(std::unique_ptr<CJS_Timer>( |
- new CJS_Timer(this, pApp, pRuntime, 0, script, dwInterval, 0))); |
+ m_Timers.push_back(std::unique_ptr<GlobalTimer>( |
+ new GlobalTimer(this, pApp, pRuntime, 0, script, dwInterval, 0))); |
v8::Local<v8::Object> pRetObj = FXJS_NewFxDynamicObj( |
pRuntime->GetIsolate(), pRuntime, CJS_TimerObj::g_nObjDefnID); |
@@ -408,8 +530,8 @@ FX_BOOL app::setTimeOut(IJS_Context* cc, |
uint32_t dwTimeOut = params.size() > 1 ? params[1].ToInt() : 1000; |
CJS_Runtime* pRuntime = pContext->GetJSRuntime(); |
CPDFDoc_Environment* pApp = pRuntime->GetReaderApp(); |
- m_Timers.push_back(std::unique_ptr<CJS_Timer>( |
- new CJS_Timer(this, pApp, pRuntime, 1, script, dwTimeOut, dwTimeOut))); |
+ m_Timers.push_back(std::unique_ptr<GlobalTimer>( |
+ new GlobalTimer(this, pApp, pRuntime, 1, script, dwTimeOut, dwTimeOut))); |
v8::Local<v8::Object> pRetObj = FXJS_NewFxDynamicObj( |
pRuntime->GetIsolate(), pRuntime, CJS_TimerObj::g_nObjDefnID); |
@@ -468,7 +590,7 @@ void app::ClearTimerCommon(const CJS_Value& param) { |
if (!pTimerObj) |
return; |
- CJS_Timer::Cancel(pTimerObj->GetTimerID()); |
+ GlobalTimer::Cancel(pTimerObj->GetTimerID()); |
} |
FX_BOOL app::execMenuItem(IJS_Context* cc, |
@@ -478,15 +600,15 @@ FX_BOOL app::execMenuItem(IJS_Context* cc, |
return FALSE; |
} |
-void app::TimerProc(CJS_Timer* pTimer) { |
+void app::TimerProc(GlobalTimer* pTimer) { |
CJS_Runtime* pRuntime = pTimer->GetRuntime(); |
if (pRuntime && (!pTimer->IsOneShot() || pTimer->GetTimeOut() > 0)) |
RunJsScript(pRuntime, pTimer->GetJScript()); |
} |
-void app::CancelProc(CJS_Timer* pTimer) { |
+void app::CancelProc(GlobalTimer* pTimer) { |
auto iter = std::find_if(m_Timers.begin(), m_Timers.end(), |
- [pTimer](const std::unique_ptr<CJS_Timer>& that) { |
+ [pTimer](const std::unique_ptr<GlobalTimer>& that) { |
return pTimer == that.get(); |
}); |