Move CPWL_Timer to CFX_Timer.

First step in consolidating a bunch of distinct timer
implementations. Add unit test while we're at it.

Change-Id: I05a58bb486a392773667de8a2e3a0781ebc03512
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/59050
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index b582acc..83cf2f7 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -22,6 +22,8 @@
     "cfx_readonlymemorystream.h",
     "cfx_seekablestreamproxy.cpp",
     "cfx_seekablestreamproxy.h",
+    "cfx_timer.cpp",
+    "cfx_timer.h",
     "cfx_utf8decoder.cpp",
     "cfx_utf8decoder.h",
     "cfx_utf8encoder.cpp",
@@ -134,6 +136,7 @@
     "bytestring_unittest.cpp",
     "cfx_bitstream_unittest.cpp",
     "cfx_seekablestreamproxy_unittest.cpp",
+    "cfx_timer_unittest.cpp",
     "cfx_widetextbuf_unittest.cpp",
     "fx_bidi_unittest.cpp",
     "fx_coordinates_unittest.cpp",
diff --git a/fpdfsdk/pwl/cpwl_timer.cpp b/core/fxcrt/cfx_timer.cpp
similarity index 68%
rename from fpdfsdk/pwl/cpwl_timer.cpp
rename to core/fxcrt/cfx_timer.cpp
index a501c7f..057cda6 100644
--- a/fpdfsdk/pwl/cpwl_timer.cpp
+++ b/core/fxcrt/cfx_timer.cpp
@@ -4,23 +4,23 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
-#include "fpdfsdk/pwl/cpwl_timer.h"
+#include "core/fxcrt/cfx_timer.h"
 
 #include <map>
 
 namespace {
 
-std::map<int32_t, CPWL_Timer*>& GetPWLTimeMap() {
+std::map<int32_t, CFX_Timer*>& GetPWLTimeMap() {
   // Leak the object at shutdown.
-  static auto* timeMap = new std::map<int32_t, CPWL_Timer*>;
+  static auto* timeMap = new std::map<int32_t, CFX_Timer*>;
   return *timeMap;
 }
 
 }  // namespace
 
-CPWL_Timer::CPWL_Timer(TimerHandlerIface* pTimerHandler,
-                       CallbackIface* pCallbackIface,
-                       int32_t nInterval)
+CFX_Timer::CFX_Timer(TimerHandlerIface* pTimerHandler,
+                     CallbackIface* pCallbackIface,
+                     int32_t nInterval)
     : m_nTimerID(pTimerHandler->SetTimer(nInterval, TimerProc)),
       m_pTimerHandler(pTimerHandler),
       m_pCallbackIface(pCallbackIface) {
@@ -29,7 +29,7 @@
     GetPWLTimeMap()[m_nTimerID] = this;
 }
 
-CPWL_Timer::~CPWL_Timer() {
+CFX_Timer::~CFX_Timer() {
   if (HasValidID()) {
     m_pTimerHandler->KillTimer(m_nTimerID);
     GetPWLTimeMap().erase(m_nTimerID);
@@ -37,7 +37,7 @@
 }
 
 // static
-void CPWL_Timer::TimerProc(int32_t idEvent) {
+void CFX_Timer::TimerProc(int32_t idEvent) {
   auto it = GetPWLTimeMap().find(idEvent);
   if (it != GetPWLTimeMap().end())
     it->second->m_pCallbackIface->OnTimerFired();
diff --git a/fpdfsdk/pwl/cpwl_timer.h b/core/fxcrt/cfx_timer.h
similarity index 71%
rename from fpdfsdk/pwl/cpwl_timer.h
rename to core/fxcrt/cfx_timer.h
index 2258674..fa97dda 100644
--- a/fpdfsdk/pwl/cpwl_timer.h
+++ b/core/fxcrt/cfx_timer.h
@@ -4,15 +4,15 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
-#ifndef FPDFSDK_PWL_CPWL_TIMER_H_
-#define FPDFSDK_PWL_CPWL_TIMER_H_
+#ifndef CORE_FXCRT_CFX_TIMER_H_
+#define CORE_FXCRT_CFX_TIMER_H_
 
 #include "core/fxcrt/timerhandler_iface.h"
 #include "core/fxcrt/unowned_ptr.h"
 
-class CPWL_TimerHandler;
+class CFX_TimerHandler;
 
-class CPWL_Timer {
+class CFX_Timer {
  public:
   class CallbackIface {
    public:
@@ -20,21 +20,21 @@
     virtual void OnTimerFired() = 0;
   };
 
-  CPWL_Timer(TimerHandlerIface* pTimerHandler,
-             CallbackIface* pCallbackIface,
-             int32_t nInterval);
-  ~CPWL_Timer();
-
- private:
-  static void TimerProc(int32_t idEvent);
+  CFX_Timer(TimerHandlerIface* pTimerHandler,
+            CallbackIface* pCallbackIface,
+            int32_t nInterval);
+  ~CFX_Timer();
 
   bool HasValidID() const {
     return m_nTimerID != TimerHandlerIface::kInvalidTimerID;
   }
 
+ private:
+  static void TimerProc(int32_t idEvent);
+
   const int32_t m_nTimerID;
   UnownedPtr<TimerHandlerIface> const m_pTimerHandler;
   UnownedPtr<CallbackIface> const m_pCallbackIface;
 };
 
-#endif  // FPDFSDK_PWL_CPWL_TIMER_H_
+#endif  // CORE_FXCRT_CFX_TIMER_H_
diff --git a/core/fxcrt/cfx_timer_unittest.cpp b/core/fxcrt/cfx_timer_unittest.cpp
new file mode 100644
index 0000000..b95de72
--- /dev/null
+++ b/core/fxcrt/cfx_timer_unittest.cpp
@@ -0,0 +1,85 @@
+// Copyright 2019 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 "core/fxcrt/cfx_timer.h"
+
+#include <memory>
+
+#include "core/fxcrt/timerhandler_iface.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+
+class MockTimerScheduler : public TimerHandlerIface {
+ public:
+  MOCK_METHOD2(SetTimer, int(int32_t uElapse, TimerCallback lpTimerFunc));
+  MOCK_METHOD1(KillTimer, void(int32_t nID));
+};
+
+class MockTimerCallback : public CFX_Timer::CallbackIface {
+ public:
+  MOCK_METHOD0(OnTimerFired, void());
+};
+
+TEST(CFX_Timer, ValidTimers) {
+  TimerHandlerIface::TimerCallback fn1 = nullptr;
+  TimerHandlerIface::TimerCallback fn2 = nullptr;
+
+  MockTimerScheduler scheduler;
+  EXPECT_CALL(scheduler, SetTimer(100, _))
+      .WillOnce(DoAll(SaveArg<1>(&fn1), Return(1001)));
+  EXPECT_CALL(scheduler, SetTimer(200, _))
+      .WillOnce(DoAll(SaveArg<1>(&fn2), Return(1002)));
+  EXPECT_CALL(scheduler, KillTimer(1001));
+  EXPECT_CALL(scheduler, KillTimer(1002));
+
+  MockTimerCallback cb1;
+  EXPECT_CALL(cb1, OnTimerFired()).Times(1);
+
+  MockTimerCallback cb2;
+  EXPECT_CALL(cb2, OnTimerFired()).Times(2);
+
+  auto timer1 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb1, 100);
+  auto timer2 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb2, 200);
+  EXPECT_TRUE(timer1->HasValidID());
+  EXPECT_TRUE(timer2->HasValidID());
+
+  // Fire some timers.
+  ASSERT_TRUE(fn1);
+  ASSERT_TRUE(fn2);
+  (*fn1)(1001);
+  (*fn1)(1002);
+  (*fn1)(1002);
+}
+
+TEST(CFX_Timer, MisbehavingEmbedder) {
+  TimerHandlerIface::TimerCallback fn1 = nullptr;
+
+  MockTimerScheduler scheduler;
+  EXPECT_CALL(scheduler, SetTimer(100, _))
+      .WillOnce(DoAll(SaveArg<1>(&fn1), Return(1001)));
+  EXPECT_CALL(scheduler, KillTimer(1001));
+
+  MockTimerCallback cb1;
+  EXPECT_CALL(cb1, OnTimerFired()).Times(0);
+
+  {
+    auto timer1 = pdfium::MakeUnique<CFX_Timer>(&scheduler, &cb1, 100);
+    EXPECT_TRUE(timer1->HasValidID());
+
+    // Fire callback with bad arguments.
+    ASSERT_TRUE(fn1);
+    (*fn1)(-1);
+    (*fn1)(0);
+    (*fn1)(1002);
+  }
+
+  // Fire callback against stale timer.
+  (*fn1)(1001);
+}
diff --git a/fpdfsdk/formfiller/cffl_formfiller.h b/fpdfsdk/formfiller/cffl_formfiller.h
index 9d8aa35..93e57c5 100644
--- a/fpdfsdk/formfiller/cffl_formfiller.h
+++ b/fpdfsdk/formfiller/cffl_formfiller.h
@@ -11,11 +11,11 @@
 #include <memory>
 
 #include "core/fpdfdoc/cba_fontmap.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_fieldaction.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
-#include "fpdfsdk/pwl/cpwl_timer.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 #include "fpdfsdk/pwl/ipwl_systemhandler.h"
 
@@ -24,7 +24,7 @@
 class CPDFSDK_PageView;
 
 class CFFL_FormFiller : public CPWL_Wnd::ProviderIface,
-                        public CPWL_Timer::CallbackIface {
+                        public CFX_Timer::CallbackIface {
  public:
   CFFL_FormFiller(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                   CPDFSDK_Widget* pWidget);
@@ -86,7 +86,7 @@
   void SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag);
   void KillFocusForAnnot(uint32_t nFlag);
 
-  // CPWL_Timer::CallbackIface:
+  // CFX_Timer::CallbackIface:
   void OnTimerFired() override;
 
   // CPWL_Wnd::ProviderIface:
@@ -157,7 +157,7 @@
   bool m_bValid = false;
   UnownedPtr<CPDFSDK_FormFillEnvironment> const m_pFormFillEnv;
   UnownedPtr<CPDFSDK_Widget> m_pWidget;
-  std::unique_ptr<CPWL_Timer> m_pTimer;
+  std::unique_ptr<CFX_Timer> m_pTimer;
   std::map<CPDFSDK_PageView*, std::unique_ptr<CPWL_Wnd>> m_Maps;
 };
 
diff --git a/fpdfsdk/pwl/BUILD.gn b/fpdfsdk/pwl/BUILD.gn
index 5726629..94396e3 100644
--- a/fpdfsdk/pwl/BUILD.gn
+++ b/fpdfsdk/pwl/BUILD.gn
@@ -30,8 +30,6 @@
     "cpwl_scroll_bar.h",
     "cpwl_special_button.cpp",
     "cpwl_special_button.h",
-    "cpwl_timer.cpp",
-    "cpwl_timer.h",
     "cpwl_wnd.cpp",
     "cpwl_wnd.h",
     "ipwl_systemhandler.h",
diff --git a/fpdfsdk/pwl/cpwl_caret.cpp b/fpdfsdk/pwl/cpwl_caret.cpp
index 5be6ff0..739fd37 100644
--- a/fpdfsdk/pwl/cpwl_caret.cpp
+++ b/fpdfsdk/pwl/cpwl_caret.cpp
@@ -85,8 +85,8 @@
 
     m_ptHead = ptHead;
     m_ptFoot = ptFoot;
-    m_pTimer = pdfium::MakeUnique<CPWL_Timer>(GetTimerHandler(), this,
-                                              kCaretFlashIntervalMs);
+    m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this,
+                                             kCaretFlashIntervalMs);
 
     if (!CPWL_Wnd::SetVisible(true))
       return;
diff --git a/fpdfsdk/pwl/cpwl_caret.h b/fpdfsdk/pwl/cpwl_caret.h
index 9458ed6..71788ef 100644
--- a/fpdfsdk/pwl/cpwl_caret.h
+++ b/fpdfsdk/pwl/cpwl_caret.h
@@ -9,10 +9,10 @@
 
 #include <memory>
 
-#include "fpdfsdk/pwl/cpwl_timer.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
-class CPWL_Caret final : public CPWL_Wnd, public CPWL_Timer::CallbackIface {
+class CPWL_Caret final : public CPWL_Wnd, public CFX_Timer::CallbackIface {
  public:
   CPWL_Caret(const CreateParams& cp,
              std::unique_ptr<IPWL_SystemHandler::PerWindowData> pAttachedData);
@@ -24,7 +24,7 @@
   bool InvalidateRect(CFX_FloatRect* pRect) override;
   bool SetVisible(bool bVisible) override;
 
-  // CPWL_Timer::CallbackIface:
+  // CFX_Timer::CallbackIface:
   void OnTimerFired() override;
 
   void SetCaret(bool bVisible,
@@ -40,7 +40,7 @@
   CFX_PointF m_ptFoot;
   float m_fWidth = 0.4f;
   CFX_FloatRect m_rcInvalid;
-  std::unique_ptr<CPWL_Timer> m_pTimer;
+  std::unique_ptr<CFX_Timer> m_pTimer;
 };
 
 #endif  // FPDFSDK_PWL_CPWL_CARET_H_
diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.cpp b/fpdfsdk/pwl/cpwl_scroll_bar.cpp
index 209c465..bf4d1da 100644
--- a/fpdfsdk/pwl/cpwl_scroll_bar.cpp
+++ b/fpdfsdk/pwl/cpwl_scroll_bar.cpp
@@ -676,7 +676,7 @@
 
   NotifyScrollWindow();
   m_bMinOrMax = true;
-  m_pTimer = pdfium::MakeUnique<CPWL_Timer>(GetTimerHandler(), this, 100);
+  m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this, 100);
 }
 
 void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {}
@@ -690,7 +690,7 @@
 
   NotifyScrollWindow();
   m_bMinOrMax = false;
-  m_pTimer = pdfium::MakeUnique<CPWL_Timer>(GetTimerHandler(), this, 100);
+  m_pTimer = pdfium::MakeUnique<CFX_Timer>(GetTimerHandler(), this, 100);
 }
 
 void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {}
diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.h b/fpdfsdk/pwl/cpwl_scroll_bar.h
index 6d71734..f6bb2b9 100644
--- a/fpdfsdk/pwl/cpwl_scroll_bar.h
+++ b/fpdfsdk/pwl/cpwl_scroll_bar.h
@@ -9,8 +9,8 @@
 
 #include <memory>
 
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/pwl/cpwl_timer.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 
 struct PWL_SCROLL_INFO {
@@ -114,7 +114,7 @@
   float fSmallStep;
 };
 
-class CPWL_ScrollBar final : public CPWL_Wnd, public CPWL_Timer::CallbackIface {
+class CPWL_ScrollBar final : public CPWL_Wnd, public CFX_Timer::CallbackIface {
  public:
   CPWL_ScrollBar(
       const CreateParams& cp,
@@ -136,7 +136,7 @@
   void NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) override;
   void CreateChildWnd(const CreateParams& cp) override;
 
-  // CPWL_Timer::CallbackIface:
+  // CFX_Timer::CallbackIface:
   void OnTimerFired() override;
 
   float GetScrollBarWidth() const;
@@ -176,7 +176,7 @@
   UnownedPtr<CPWL_SBButton> m_pMinButton;
   UnownedPtr<CPWL_SBButton> m_pMaxButton;
   UnownedPtr<CPWL_SBButton> m_pPosButton;
-  std::unique_ptr<CPWL_Timer> m_pTimer;
+  std::unique_ptr<CFX_Timer> m_pTimer;
   PWL_SCROLL_PRIVATEDATA m_sData;
   bool m_bMouseDown = false;
   bool m_bMinOrMax = false;
diff --git a/fpdfsdk/pwl/cpwl_wnd.h b/fpdfsdk/pwl/cpwl_wnd.h
index 45e6955..9be574f 100644
--- a/fpdfsdk/pwl/cpwl_wnd.h
+++ b/fpdfsdk/pwl/cpwl_wnd.h
@@ -11,11 +11,11 @@
 #include <vector>
 
 #include "core/fpdfdoc/cpdf_formcontrol.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_color.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "fpdfsdk/pwl/cpwl_timer.h"
 #include "fpdfsdk/pwl/ipwl_systemhandler.h"
 
 class CPWL_Edit;