Observe CFX_Timer's timer handler objects.

Allows CFX_Timer lifetimes to be less precise in face of longer-lived
garbage-collected objects. CFX_Timers refer back to form fill
environments, which are shorter lived than the documents themselves,
so this is a good precaution even in a non-gc'ed world.

-- nest TimerHanderIface inside CFX_Timer while at it.

Bug: chromium:1137668
Change-Id: I5738db783af43eff3b23b5936cfdb82d7510c410
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/75030
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 5cd0d3a..ed22e6f 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -67,7 +67,6 @@
     "string_data_template.h",
     "string_pool_template.h",
     "string_view_template.h",
-    "timerhandler_iface.h",
     "tree_node.h",
     "unowned_ptr.h",
     "weak_ptr.h",
diff --git a/core/fxcrt/cfx_timer.cpp b/core/fxcrt/cfx_timer.cpp
index 6ffb625..ba8b3e2 100644
--- a/core/fxcrt/cfx_timer.cpp
+++ b/core/fxcrt/cfx_timer.cpp
@@ -20,21 +20,23 @@
 
 }  // namespace
 
-CFX_Timer::CFX_Timer(TimerHandlerIface* pTimerHandler,
+CFX_Timer::CFX_Timer(HandlerIface* pHandlerIface,
                      CallbackIface* pCallbackIface,
                      int32_t nInterval)
-    : m_nTimerID(pTimerHandler->SetTimer(nInterval, TimerProc)),
-      m_pTimerHandler(pTimerHandler),
-      m_pCallbackIface(pCallbackIface) {
+    : m_pHandlerIface(pHandlerIface), m_pCallbackIface(pCallbackIface) {
   ASSERT(m_pCallbackIface);
-  if (HasValidID())
-    GetPWLTimerMap()[m_nTimerID] = this;
+  if (m_pHandlerIface) {
+    m_nTimerID = m_pHandlerIface->SetTimer(nInterval, TimerProc);
+    if (HasValidID())
+      GetPWLTimerMap()[m_nTimerID] = this;
+  }
 }
 
 CFX_Timer::~CFX_Timer() {
   if (HasValidID()) {
-    m_pTimerHandler->KillTimer(m_nTimerID);
     GetPWLTimerMap().erase(m_nTimerID);
+    if (m_pHandlerIface)
+      m_pHandlerIface->KillTimer(m_nTimerID);
   }
 }
 
diff --git a/core/fxcrt/cfx_timer.h b/core/fxcrt/cfx_timer.h
index fa97dda..f245607 100644
--- a/core/fxcrt/cfx_timer.h
+++ b/core/fxcrt/cfx_timer.h
@@ -7,33 +7,46 @@
 #ifndef CORE_FXCRT_CFX_TIMER_H_
 #define CORE_FXCRT_CFX_TIMER_H_
 
-#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
-class CFX_TimerHandler;
-
 class CFX_Timer {
  public:
+  // HandlerIface is implemented by upper layers that actually perform
+  // the system-dependent actions of scheduling and triggering timers.
+  class HandlerIface : public Observable {
+   public:
+    static constexpr int32_t kInvalidTimerID = 0;
+    using TimerCallback = void (*)(int32_t idEvent);
+
+    virtual ~HandlerIface() = default;
+
+    virtual int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) = 0;
+    virtual void KillTimer(int32_t nTimerID) = 0;
+  };
+
+  // CallbackIface is implemented by layers that want to perform a
+  // specific action on timer expiry.
   class CallbackIface {
    public:
     virtual ~CallbackIface() = default;
     virtual void OnTimerFired() = 0;
   };
 
-  CFX_Timer(TimerHandlerIface* pTimerHandler,
+  CFX_Timer(HandlerIface* pTimerHandler,
             CallbackIface* pCallbackIface,
             int32_t nInterval);
   ~CFX_Timer();
 
   bool HasValidID() const {
-    return m_nTimerID != TimerHandlerIface::kInvalidTimerID;
+    return m_nTimerID != HandlerIface::kInvalidTimerID;
   }
 
  private:
   static void TimerProc(int32_t idEvent);
 
-  const int32_t m_nTimerID;
-  UnownedPtr<TimerHandlerIface> const m_pTimerHandler;
+  int32_t m_nTimerID = HandlerIface::kInvalidTimerID;
+  ObservedPtr<HandlerIface> m_pHandlerIface;
   UnownedPtr<CallbackIface> const m_pCallbackIface;
 };
 
diff --git a/core/fxcrt/cfx_timer_unittest.cpp b/core/fxcrt/cfx_timer_unittest.cpp
index 5a7963f..6e8b3ea 100644
--- a/core/fxcrt/cfx_timer_unittest.cpp
+++ b/core/fxcrt/cfx_timer_unittest.cpp
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "core/fxcrt/timerhandler_iface.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -15,7 +14,7 @@
 using testing::Return;
 using testing::SaveArg;
 
-class MockTimerScheduler : public TimerHandlerIface {
+class MockTimerScheduler : public CFX_Timer::HandlerIface {
  public:
   MOCK_METHOD2(SetTimer, int(int32_t uElapse, TimerCallback lpTimerFunc));
   MOCK_METHOD1(KillTimer, void(int32_t nID));
@@ -27,8 +26,8 @@
 };
 
 TEST(CFX_Timer, ValidTimers) {
-  TimerHandlerIface::TimerCallback fn1 = nullptr;
-  TimerHandlerIface::TimerCallback fn2 = nullptr;
+  CFX_Timer::HandlerIface::TimerCallback fn1 = nullptr;
+  CFX_Timer::HandlerIface::TimerCallback fn2 = nullptr;
 
   MockTimerScheduler scheduler;
   EXPECT_CALL(scheduler, SetTimer(100, _))
@@ -58,7 +57,7 @@
 }
 
 TEST(CFX_Timer, MisbehavingEmbedder) {
-  TimerHandlerIface::TimerCallback fn1 = nullptr;
+  CFX_Timer::HandlerIface::TimerCallback fn1 = nullptr;
 
   MockTimerScheduler scheduler;
   EXPECT_CALL(scheduler, SetTimer(100, _))
diff --git a/core/fxcrt/timerhandler_iface.h b/core/fxcrt/timerhandler_iface.h
deleted file mode 100644
index 04e781d..0000000
--- a/core/fxcrt/timerhandler_iface.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_TIMERHANDLER_IFACE_H_
-#define CORE_FXCRT_TIMERHANDLER_IFACE_H_
-
-#include "core/fxcrt/fx_system.h"
-
-namespace fxcrt {
-
-class TimerHandlerIface {
- public:
-  static constexpr int32_t kInvalidTimerID = 0;
-  using TimerCallback = void (*)(int32_t idEvent);
-
-  virtual ~TimerHandlerIface() = default;
-
-  virtual int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) = 0;
-  virtual void KillTimer(int32_t nTimerID) = 0;
-};
-
-}  // namespace fxcrt
-
-using fxcrt::TimerHandlerIface;
-
-#endif  // CORE_FXCRT_TIMERHANDLER_IFACE_H_
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.cpp b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
index 63a5114..47d9cc1 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.cpp
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
@@ -352,7 +352,7 @@
                                           TimerCallback lpTimerFunc) {
   if (m_pInfo && m_pInfo->FFI_SetTimer)
     return m_pInfo->FFI_SetTimer(m_pInfo, uElapse, lpTimerFunc);
-  return TimerHandlerIface::kInvalidTimerID;
+  return CFX_Timer::HandlerIface::kInvalidTimerID;
 }
 
 void CPDFSDK_FormFillEnvironment::KillTimer(int nTimerID) {
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.h b/fpdfsdk/cpdfsdk_formfillenvironment.h
index 1ceff5d..6f26b92 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.h
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.h
@@ -14,8 +14,8 @@
 #include "core/fpdfapi/page/cpdf_occontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/timerhandler_iface.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/pwl/ipwl_systemhandler.h"
@@ -43,8 +43,7 @@
 // hierarcy back to the form fill environment itself, so as to flag any
 // lingering lifetime issues via the memory tools.
 
-class CPDFSDK_FormFillEnvironment final : public Observable,
-                                          public TimerHandlerIface,
+class CPDFSDK_FormFillEnvironment final : public CFX_Timer::HandlerIface,
                                           public IPWL_SystemHandler {
  public:
   CPDFSDK_FormFillEnvironment(
@@ -196,7 +195,7 @@
 
   WideString GetFilePath() const;
   ByteString GetAppName() const { return ByteString(); }
-  TimerHandlerIface* GetTimerHandler() { return this; }
+  CFX_Timer::HandlerIface* GetTimerHandler() { return this; }
   IPWL_SystemHandler* GetSysHandler() { return this; }
   FPDF_FORMFILLINFO* GetFormFillInfo() const { return m_pInfo; }
   void SubmitForm(pdfium::span<uint8_t> form_data, const WideString& URL);
diff --git a/fpdfsdk/formfiller/cffl_formfiller.cpp b/fpdfsdk/formfiller/cffl_formfiller.cpp
index 7856254..76be5aa 100644
--- a/fpdfsdk/formfiller/cffl_formfiller.cpp
+++ b/fpdfsdk/formfiller/cffl_formfiller.cpp
@@ -336,7 +336,7 @@
     dwCreateFlags |= PWS_AUTOFONTSIZE;
 
   cp.dwFlags = dwCreateFlags;
-  cp.pTimerHandler = m_pFormFillEnv->GetTimerHandler();
+  cp.pTimerHandler.Reset(m_pFormFillEnv->GetTimerHandler());
   cp.pSystemHandler = m_pFormFillEnv->GetSysHandler();
   return cp;
 }
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
index 89ceeea..3af1a91 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
@@ -375,7 +375,7 @@
          m_pFormFillEnv->PutRequestURL(wsURL, wsData, wsEncode);
 }
 
-TimerHandlerIface* CPDFXFA_Context::GetTimerHandler() const {
+CFX_Timer::HandlerIface* CPDFXFA_Context::GetTimerHandler() const {
   return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
 }
 
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.h b/fpdfsdk/fpdfxfa/cpdfxfa_context.h
index a64ac69..389ba79 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.h
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.h
@@ -11,9 +11,9 @@
 #include <vector>
 
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/timerhandler_iface.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
@@ -99,7 +99,7 @@
   bool PutRequestURL(const WideString& wsURL,
                      const WideString& wsData,
                      const WideString& wsEncode) override;
-  TimerHandlerIface* GetTimerHandler() const override;
+  CFX_Timer::HandlerIface* GetTimerHandler() const override;
   cppgc::Heap* GetGCHeap() const override;
 
   bool SaveDatasetsPackage(const RetainPtr<IFX_SeekableStream>& pStream);
diff --git a/fpdfsdk/pwl/cpwl_wnd.h b/fpdfsdk/pwl/cpwl_wnd.h
index b3730a0..8412f0a 100644
--- a/fpdfsdk/pwl/cpwl_wnd.h
+++ b/fpdfsdk/pwl/cpwl_wnd.h
@@ -114,7 +114,7 @@
 
     // Required:
     CFX_FloatRect rcRectWnd;
-    UnownedPtr<TimerHandlerIface> pTimerHandler;
+    ObservedPtr<CFX_Timer::HandlerIface> pTimerHandler;
     UnownedPtr<IPWL_SystemHandler> pSystemHandler;
     UnownedPtr<IPVT_FontMap> pFontMap;
     ObservedPtr<ProviderIface> pProvider;
@@ -274,7 +274,7 @@
   bool IsNotifying() const { return m_bNotifying; }
   bool IsValid() const { return m_bCreated; }
   CreateParams* GetCreationParams() { return &m_CreationParams; }
-  TimerHandlerIface* GetTimerHandler() const {
+  CFX_Timer::HandlerIface* GetTimerHandler() const {
     return m_CreationParams.pTimerHandler.Get();
   }
   IPWL_SystemHandler* GetSystemHandler() const {
diff --git a/fxjs/cjs_runtime.cpp b/fxjs/cjs_runtime.cpp
index d2c30d3..443f1a5 100644
--- a/fxjs/cjs_runtime.cpp
+++ b/fxjs/cjs_runtime.cpp
@@ -136,7 +136,7 @@
                                      : m_EventContextArray.back().get();
 }
 
-TimerHandlerIface* CJS_Runtime::GetTimerHandler() const {
+CFX_Timer::HandlerIface* CJS_Runtime::GetTimerHandler() const {
   return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
 }
 
diff --git a/fxjs/cjs_runtime.h b/fxjs/cjs_runtime.h
index b3f8ef6..1802ebd 100644
--- a/fxjs/cjs_runtime.h
+++ b/fxjs/cjs_runtime.h
@@ -12,8 +12,8 @@
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/cfx_timer.h"
 #include "core/fxcrt/observed_ptr.h"
-#include "core/fxcrt/timerhandler_iface.h"
 #include "fxjs/cfxjs_engine.h"
 #include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/ijs_runtime.h"
@@ -39,7 +39,7 @@
       const WideString& script) override;
 
   CJS_EventContext* GetCurrentEventContext() const;
-  TimerHandlerIface* GetTimerHandler() const;
+  CFX_Timer::HandlerIface* GetTimerHandler() const;
 
   // Returns true if the event isn't already found in the set.
   bool AddEventToSet(const FieldEvent& event);
diff --git a/fxjs/global_timer.cpp b/fxjs/global_timer.cpp
index ded2596..6850d0a 100644
--- a/fxjs/global_timer.cpp
+++ b/fxjs/global_timer.cpp
@@ -8,7 +8,7 @@
 
 #include <map>
 
-#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "fxjs/cjs_app.h"
 #include "third_party/base/no_destructor.h"
 #include "third_party/base/stl_util.h"
@@ -88,5 +88,5 @@
 }
 
 bool GlobalTimer::HasValidID() const {
-  return m_nTimerID != TimerHandlerIface::kInvalidTimerID;
+  return m_nTimerID != CFX_Timer::HandlerIface::kInvalidTimerID;
 }
diff --git a/testing/resources/javascript/xfa_specific/bug_1137668.evt b/testing/resources/javascript/xfa_specific/bug_1137668.evt
new file mode 100644
index 0000000..d4b67d8
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1137668.evt
@@ -0,0 +1,9 @@
+keycode,9,shift
+mousedown,left,50,50
+keycode,9
+mousemove,50,50
+mousedown,left,50,50
+keycode,9
+mousemove,20,20
+mousedown,left,20,20
+mousedown,right,20,20
diff --git a/testing/resources/javascript/xfa_specific/bug_1137668.in b/testing/resources/javascript/xfa_specific/bug_1137668.in
new file mode 100644
index 0000000..11014e7
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1137668.in
@@ -0,0 +1,338 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+<template>
+  <subform name="s1">
+    <subform name="s5">
+      <subform name="s6">
+        <event activity="enter">
+          <script contentType="application/x-javascript">
+            xfa.template.remerge();
+            xfa.form.remerge();
+          </script>
+        </event>
+        <field name="field_31">
+          <ui>
+            <button highlight="push"/>
+          </ui>
+          <caption>
+            <value>
+              <text>
+                Button
+              </text>
+            </value>
+          </caption>
+          <items>
+            <text name="down">
+              Down Text
+            </text>
+            <text name="rollover">
+              Rollover Text
+            </text>
+          </items>
+        </field>
+        <field name="field_32">
+          <ui>
+            <barcode/>
+          </ui>
+          <value>
+            <text>
+              test
+            </text>
+          </value>
+        </field>
+        <field name="field_33">
+          <ui>
+            <checkButton shape="round">
+              <border>
+                <edge/>
+              </border>
+            </checkButton>
+          </ui>
+          <items>
+            <integer>
+              2
+            </integer>
+          </items>
+          <value>
+            <text>
+              Select 2
+            </text>
+          </value>
+          <caption placement="left">
+            <value>
+              <text>
+                Option 2
+              </text>
+            </value>
+          </caption>
+        </field>
+        <field name="field_34">
+          <ui>
+            <textEdit/>
+          </ui>
+          <value>
+            <text>
+              CLGT.
+            </text>
+          </value>
+          <event activity="ready">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <event activity="enter">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <event activity="initialize">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+        </field>
+        <field name="field_35">
+          <validate>
+            <script contentType="application/x-javascript">
+            </script>
+          </validate>
+          <calculate>
+            <script contentType="application/x-javascript">
+            </script>
+          </calculate>
+          <ui>
+            <choiceList open="onEntry">
+              <border><edge/></border>
+            </choiceList>
+          </ui>
+          <items save="1">
+            <text>apples</text>
+            <text>bananas</text>
+            <text>pears</text>
+          </items>
+          <value>
+            <text>apples</text>
+          </value>
+          <event activity="docClose">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <event activity="initialize">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+          <event activity="change">
+            <script contentType="application/x-javascript">
+            </script>
+          </event>
+        </field>
+        <subform name="s7">
+          <field name="field_36">
+            <ui>
+              <dateTimeEdit>
+                <comb numberOfCells="8"/>
+                <border hand="right">
+                  <edge/>
+                </border>
+              </dateTimeEdit>
+            </ui>
+          </field>
+          <field name="field_37">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <button highlight="push"/>
+            </ui>
+            <caption>
+              <value>
+                <text>
+                  Button
+                </text>
+              </value>
+            </caption>
+            <items>
+              <text name="down">
+                Down Text
+              </text>
+              <text name="rollover">
+                Rollover Text
+              </text>
+            </items>
+            <event activity="docClose">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="change">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="exit">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+          <field name="field_38">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <barcode/>
+            </ui>
+            <value>
+              <text>
+                test
+              </text>
+            </value>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="change">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="ready">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+          <field name="field_39">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <checkButton shape="round">
+                <border>
+                  <edge/>
+                </border>
+              </checkButton>
+            </ui>
+            <items>
+              <integer>
+                2
+              </integer>
+            </items>
+            <value>
+              <text>
+                Select 2
+              </text>
+            </value>
+            <caption placement="left">
+              <value>
+                <text>
+                  Option 2
+                </text>
+              </value>
+            </caption>
+            <event activity="enter">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="ready">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="change">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+          <field name="field_40">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <textEdit/>
+            </ui>
+            <value>
+              <text>
+                CLGT.
+              </text>
+            </value>
+            <event activity="ready">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="docClose">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="exit">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+          <field name="field_41">
+            <validate>
+              <script contentType="application/x-javascript">
+              </script>
+            </validate>
+            <calculate>
+              <script contentType="application/x-javascript">
+              </script>
+            </calculate>
+            <ui>
+              <choiceList open="onEntry">
+                <border><edge/></border>
+              </choiceList>
+            </ui>
+            <items save="1">
+              <text>apples</text>
+              <text>bananas</text>
+              <text>pears</text>
+            </items>
+            <value>
+              <text>apples</text>
+            </value>
+            <event activity="docClose">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+            <event activity="initialize">
+              <script contentType="application/x-javascript">
+              </script>
+            </event>
+          </field>
+        </subform>
+      </subform>
+    </subform>
+  </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/xfa/fwl/cfwl_app.h b/xfa/fwl/cfwl_app.h
index 1539fe5..3a9a474 100644
--- a/xfa/fwl/cfwl_app.h
+++ b/xfa/fwl/cfwl_app.h
@@ -7,7 +7,7 @@
 #ifndef XFA_FWL_CFWL_APP_H_
 #define XFA_FWL_CFWL_APP_H_
 
-#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "fxjs/gc/heap.h"
 #include "v8/include/cppgc/garbage-collected.h"
 #include "v8/include/cppgc/member.h"
@@ -33,7 +33,7 @@
    public:
     virtual ~AdapterIface() = default;
     virtual CFWL_WidgetMgr::AdapterIface* GetWidgetMgrAdapter() = 0;
-    virtual TimerHandlerIface* GetTimerHandler() = 0;
+    virtual CFX_Timer::HandlerIface* GetTimerHandler() = 0;
     virtual IFWL_ThemeProvider* GetThemeProvider() = 0;
     virtual cppgc::Heap* GetHeap() = 0;
   };
@@ -46,7 +46,7 @@
   CFWL_WidgetMgr::AdapterIface* GetWidgetMgrAdapter() const {
     return m_pAdapter->GetWidgetMgrAdapter();
   }
-  TimerHandlerIface* GetTimerHandler() const {
+  CFX_Timer::HandlerIface* GetTimerHandler() const {
     return m_pAdapter->GetTimerHandler();
   }
   IFWL_ThemeProvider* GetThemeProvider() const {
diff --git a/xfa/fxfa/cxfa_ffapp.cpp b/xfa/fxfa/cxfa_ffapp.cpp
index 28b6a91..aeca8f7 100644
--- a/xfa/fxfa/cxfa_ffapp.cpp
+++ b/xfa/fxfa/cxfa_ffapp.cpp
@@ -49,7 +49,7 @@
   return m_pAdapterWidgetMgr;
 }
 
-TimerHandlerIface* CXFA_FFApp::GetTimerHandler() {
+CFX_Timer::HandlerIface* CXFA_FFApp::GetTimerHandler() {
   return m_pProvider->GetTimerHandler();
 }
 
diff --git a/xfa/fxfa/cxfa_ffapp.h b/xfa/fxfa/cxfa_ffapp.h
index e9e1fc9..737a6ad 100644
--- a/xfa/fxfa/cxfa_ffapp.h
+++ b/xfa/fxfa/cxfa_ffapp.h
@@ -29,7 +29,7 @@
   // CFWL_App::AdapterIface:
   void Trace(cppgc::Visitor* visitor) const override;
   CFWL_WidgetMgr::AdapterIface* GetWidgetMgrAdapter() override;
-  TimerHandlerIface* GetTimerHandler() override;
+  CFX_Timer::HandlerIface* GetTimerHandler() override;
   IFWL_ThemeProvider* GetThemeProvider() override;
   cppgc::Heap* GetHeap() override;
 
diff --git a/xfa/fxfa/fxfa.h b/xfa/fxfa/fxfa.h
index 61819a4..4777972 100644
--- a/xfa/fxfa/fxfa.h
+++ b/xfa/fxfa/fxfa.h
@@ -193,7 +193,7 @@
                              const WideString& wsData,
                              const WideString& wsEncode) = 0;
 
-  virtual TimerHandlerIface* GetTimerHandler() const = 0;
+  virtual CFX_Timer::HandlerIface* GetTimerHandler() const = 0;
   virtual cppgc::Heap* GetGCHeap() const = 0;
 };