Update pdfium to Chrome 114.0.5735.130 pdfium

pdfium last commit id: 9505810f6

Bug: 279055389
Test: Build the code and flash the device and check Print functionality
Test: atest FrameworksCoreTests
Test: atest CtsPrintTestCases
Test: atest CtsPdfTestCases
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3e2fb7d98efb4ba7b51fd84e9a0ae04f8c0f7805)
Merged-In: I2efabeec0d0fa3925bcbeebf36031cee6f7f9fc4
Change-Id: I2efabeec0d0fa3925bcbeebf36031cee6f7f9fc4
diff --git a/fxjs/Android.bp b/fxjs/Android.bp
index 9616816..13d7b15 100644
--- a/fxjs/Android.bp
+++ b/fxjs/Android.bp
@@ -4,11 +4,8 @@
 
     visibility: ["//external/pdfium:__subpackages__"],
 
-    header_libs: [
-        "libpdfium-constants",
-    ],
-
     static_libs: [
+        "libpdfium-constants",
         "libpdfium-fdrm",
         "libpdfium-page",
         "libpdfium-parser",
diff --git a/fxjs/BUILD.gn b/fxjs/BUILD.gn
index 2fb819c..e3f62ff 100644
--- a/fxjs/BUILD.gn
+++ b/fxjs/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -15,8 +15,9 @@
     "ijs_runtime.cpp",
     "ijs_runtime.h",
   ]
-  configs += [ "../:pdfium_core_config" ]
+  configs += [ "../:pdfium_strict_config" ]
   deps = [ "../core/fxcrt" ]
+  public_deps = []
   visibility = [ "../*" ]
 
   if (pdf_enable_v8) {
@@ -27,6 +28,8 @@
       "cfx_keyvalue.h",
       "cfx_v8.cpp",
       "cfx_v8.h",
+      "cfx_v8_array_buffer_allocator.cpp",
+      "cfx_v8_array_buffer_allocator.h",
       "cfxjs_engine.cpp",
       "cfxjs_engine.h",
       "cjs_annot.cpp",
@@ -49,8 +52,6 @@
       "cjs_event.h",
       "cjs_event_context.cpp",
       "cjs_event_context.h",
-      "cjs_eventrecorder.cpp",
-      "cjs_eventrecorder.h",
       "cjs_field.cpp",
       "cjs_field.h",
       "cjs_font.cpp",
@@ -89,6 +90,8 @@
       "cjs_zoomtype.h",
       "fx_date_helpers.cpp",
       "fx_date_helpers.h",
+      "fxv8.cpp",
+      "fxv8.h",
       "global_timer.cpp",
       "global_timer.h",
       "js_define.cpp",
@@ -108,12 +111,13 @@
       "//v8:v8_libplatform",
     ]
     configs += [ "//v8:external_startup_data" ]
-    public_deps = [ "//v8" ]
+    public_deps += [
+      "../core/fxcrt",
+      "//v8",
+    ]
 
     if (pdf_enable_xfa) {
       sources += [
-        "xfa/cfxjse_arguments.cpp",
-        "xfa/cfxjse_arguments.h",
         "xfa/cfxjse_class.cpp",
         "xfa/cfxjse_class.h",
         "xfa/cfxjse_context.cpp",
@@ -124,6 +128,10 @@
         "xfa/cfxjse_formcalc_context.h",
         "xfa/cfxjse_isolatetracker.cpp",
         "xfa/cfxjse_isolatetracker.h",
+        "xfa/cfxjse_mapmodule.cpp",
+        "xfa/cfxjse_mapmodule.h",
+        "xfa/cfxjse_nodehelper.cpp",
+        "xfa/cfxjse_nodehelper.h",
         "xfa/cfxjse_resolveprocessor.cpp",
         "xfa/cfxjse_resolveprocessor.h",
         "xfa/cfxjse_runtimedata.cpp",
@@ -203,27 +211,65 @@
         "xfa/jse_define.h",
       ]
       deps += [
-        "../xfa/fgas",
-        "../xfa/fxfa/fm2js",
+        ":gc",
+        "../xfa/fgas/crt",
+        "../xfa/fxfa/formcalc",
       ]
     }
   }
 }
 
 if (pdf_enable_v8) {
+  if (pdf_enable_xfa) {
+    source_set("gc") {
+      sources = [
+        "gc/container_trace.h",
+        "gc/gced_tree_node.h",
+        "gc/gced_tree_node_mixin.h",
+        "gc/heap.cpp",
+        "gc/heap.h",
+      ]
+      configs += [ "../:pdfium_strict_config" ]
+      deps = [
+        "../core/fxcrt",
+        "//v8:v8_libplatform",
+      ]
+      public_deps = [ "//v8:cppgc" ]
+    }
+  }
+}
+
+if (pdf_enable_v8) {
   pdfium_unittest_source_set("unittests") {
     sources = [
       "cfx_globaldata_unittest.cpp",
       "cfx_v8_unittest.cpp",
-      "cfx_v8_unittest.h",
       "cfxjs_engine_unittest.cpp",
       "cjs_publicmethods_unittest.cpp",
       "cjs_util_unittest.cpp",
       "fx_date_helpers_unittest.cpp",
     ]
     configs = [ "//v8:external_startup_data" ]
-    deps = [ ":fxjs" ]
+    deps = [
+      ":fxjs",
+      "../core/fxcrt:unit_test_support",
+    ]
     pdfium_root_dir = "../"
+    if (pdf_enable_xfa) {
+      sources += [
+        "gc/container_trace_unittest.cpp",
+        "gc/gced_tree_node_mixin_unittest.cpp",
+        "gc/gced_tree_node_unittest.cpp",
+        "gc/heap_unittest.cpp",
+        "gc/move_unittest.cpp",
+        "xfa/cfxjse_formcalc_context_unittest.cpp",
+        "xfa/cfxjse_mapmodule_unittest.cpp",
+      ]
+      deps += [
+        ":gc",
+        "../xfa/fxfa/parser",
+      ]
+    }
   }
 
   pdfium_embeddertest_source_set("embeddertests") {
@@ -237,7 +283,6 @@
       "../fpdfsdk",
     ]
     pdfium_root_dir = "../"
-
     if (pdf_enable_xfa) {
       sources += [
         "xfa/cfxjse_app_embeddertest.cpp",
@@ -245,8 +290,12 @@
         "xfa/cfxjse_value_embeddertest.cpp",
         "xfa/cjx_hostpseudomodel_embeddertest.cpp",
         "xfa/cjx_list_embeddertest.cpp",
+        "xfa/cjx_object_embeddertest.cpp",
       ]
-      deps += [ "../xfa/fxfa" ]
+      deps += [
+        ":gc",
+        "../xfa/fxfa",
+      ]
     }
   }
 }
diff --git a/fxjs/README b/fxjs/README
index a1cfe32..59b55a2 100644
--- a/fxjs/README
+++ b/fxjs/README
@@ -20,20 +20,20 @@
 objects, regardless of the FXJS/FXJSE distinction.  Slot 0 is the
 tag and contains either:
   kPerObjectDataTag for FXJS objects, or
-  g_FXJSEHostObjectTag for FXJSE Host objects, or
-  g_FXJSEProxyObjectTag for a global proxy object under FXJSE, or
+  kFXJSEHostObjectTag for FXJSE Host objects, or
+  kFXJSEProxyObjectTag for a global proxy object under FXJSE, or
   One of 4 specific FXJSE_CLASS_DESCRIPTOR globals for FXJSE classes:
-    GlobalClassDescriptor
-    NormalClassDescriptor
-    VariablesClassDescriptor
-    formcalc_fm2js_descriptor
+    kGlobalClassDescriptor
+    kNormalClassDescriptor
+    kVariablesClassDescriptor
+    kFormCalcDescriptor
 
 Slot 1's contents are determined by these tags:
   kPerObjectDataTag means an aligned pointer to CFXJS_PerObjectData.
-  g_FXJSEHostObjectTag means an aligned pointer to CFXJSE_HostObject.
-  g_FXJSEProxyObjectTag means nullptr, and to check the prototype instead.
+  kFXJSEHostObjectTag means an aligned pointer to CFXJSE_HostObject.
+  kFXJSEProxyObjectTag means nullptr, and to check the prototype instead.
   A FXJSE_CLASS_DESCRIPTOR pointer means to expect an actual v8 function
-  object (or a string naming that function),  and not an aligned pointer.
+  object (or a string naming that function), and not an aligned pointer.
 
 Because PDFium uses V8 for various unrelated purposes, there may be up to
 four v8::Contexts (JS Global Objects) associated with each document. One is
diff --git a/fxjs/cfx_globaldata.cpp b/fxjs/cfx_globaldata.cpp
index 9131f02..59b43c1 100644
--- a/fxjs/cfx_globaldata.cpp
+++ b/fxjs/cfx_globaldata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,8 @@
 #include <utility>
 
 #include "core/fdrm/fx_crypt.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -40,44 +40,41 @@
 
 void MakeNameTypeString(const ByteString& name,
                         CFX_Value::DataType eType,
-                        CFX_BinaryBuf* result) {
-  uint32_t dwNameLen = (uint32_t)name.GetLength();
-  result->AppendBlock(&dwNameLen, sizeof(uint32_t));
+                        BinaryBuffer* result) {
+  uint32_t dwNameLen = pdfium::base::checked_cast<uint32_t>(name.GetLength());
+  result->AppendUint32(dwNameLen);
   result->AppendString(name);
-
-  uint16_t wType = static_cast<uint16_t>(eType);
-  result->AppendBlock(&wType, sizeof(uint16_t));
+  result->AppendUint16(static_cast<uint16_t>(eType));
 }
 
 bool MakeByteString(const ByteString& name,
                     const CFX_KeyValue& pData,
-                    CFX_BinaryBuf* result) {
+                    BinaryBuffer* result) {
   switch (pData.nType) {
-    case CFX_Value::DataType::NUMBER: {
+    case CFX_Value::DataType::kNumber: {
       MakeNameTypeString(name, pData.nType, result);
-      double dData = pData.dData;
-      result->AppendBlock(&dData, sizeof(double));
+      result->AppendDouble(pData.dData);
       return true;
     }
-    case CFX_Value::DataType::BOOLEAN: {
+    case CFX_Value::DataType::kBoolean: {
       MakeNameTypeString(name, pData.nType, result);
-      uint16_t wData = static_cast<uint16_t>(pData.bData);
-      result->AppendBlock(&wData, sizeof(uint16_t));
+      result->AppendUint16(static_cast<uint16_t>(pData.bData));
       return true;
     }
-    case CFX_Value::DataType::STRING: {
+    case CFX_Value::DataType::kString: {
       MakeNameTypeString(name, pData.nType, result);
-      uint32_t dwDataLen = (uint32_t)pData.sData.GetLength();
-      result->AppendBlock(&dwDataLen, sizeof(uint32_t));
+      uint32_t dwDataLen =
+          pdfium::base::checked_cast<uint32_t>(pData.sData.GetLength());
+      result->AppendUint32(dwDataLen);
       result->AppendString(pData.sData);
       return true;
     }
-    case CFX_Value::DataType::NULLOBJ: {
+    case CFX_Value::DataType::kNull: {
       MakeNameTypeString(name, pData.nType, result);
       return true;
     }
     // Arrays don't get persisted per JS spec page 484.
-    case CFX_Value::DataType::OBJECT:
+    case CFX_Value::DataType::kObject:
     default:
       break;
   }
@@ -135,13 +132,13 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::NUMBER;
+    pData->data.nType = CFX_Value::DataType::kNumber;
     pData->data.dData = dData;
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::NUMBER;
+  pNewData->data.nType = CFX_Value::DataType::kNumber;
   pNewData->data.dData = dData;
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
@@ -153,13 +150,13 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::BOOLEAN;
+    pData->data.nType = CFX_Value::DataType::kBoolean;
     pData->data.bData = bData;
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::BOOLEAN;
+  pNewData->data.nType = CFX_Value::DataType::kBoolean;
   pNewData->data.bData = bData;
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
@@ -171,13 +168,13 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::STRING;
+    pData->data.nType = CFX_Value::DataType::kString;
     pData->data.sData = sData;
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::STRING;
+  pNewData->data.nType = CFX_Value::DataType::kString;
   pNewData->data.sData = sData;
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
@@ -190,13 +187,13 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::OBJECT;
+    pData->data.nType = CFX_Value::DataType::kObject;
     pData->data.objData = std::move(array);
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::OBJECT;
+  pNewData->data.nType = CFX_Value::DataType::kObject;
   pNewData->data.objData = std::move(array);
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
@@ -207,12 +204,12 @@
 
   CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
   if (pData) {
-    pData->data.nType = CFX_Value::DataType::NULLOBJ;
+    pData->data.nType = CFX_Value::DataType::kNull;
     return;
   }
-  auto pNewData = pdfium::MakeUnique<CFX_GlobalData::Element>();
+  auto pNewData = std::make_unique<CFX_GlobalData::Element>();
   pNewData->data.sKey = std::move(sPropName);
-  pNewData->data.nType = CFX_Value::DataType::NULLOBJ;
+  pNewData->data.nType = CFX_Value::DataType::kNull;
   m_arrayGlobalData.push_back(std::move(pNewData));
 }
 
@@ -242,7 +239,7 @@
 }
 
 int32_t CFX_GlobalData::GetSize() const {
-  return pdfium::CollectionSize<int32_t>(m_arrayGlobalData);
+  return fxcrt::CollectionSize<int32_t>(m_arrayGlobalData);
 }
 
 CFX_GlobalData::Element* CFX_GlobalData::GetAt(int index) {
@@ -258,7 +255,7 @@
   bool ret;
   {
     // Span can't outlive call to BufferDone().
-    Optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
+    absl::optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
     if (!buffer.has_value() || buffer.value().empty())
       return false;
 
@@ -312,7 +309,7 @@
     p += sizeof(uint16_t);
 
     switch (wDataType) {
-      case CFX_Value::DataType::NUMBER: {
+      case CFX_Value::DataType::kNumber: {
         double dData = 0;
         switch (wVersion) {
           case 1: {
@@ -328,13 +325,13 @@
         SetGlobalVariableNumber(sEntry, dData);
         SetGlobalVariablePersistent(sEntry, true);
       } break;
-      case CFX_Value::DataType::BOOLEAN: {
+      case CFX_Value::DataType::kBoolean: {
         uint16_t wData = *((uint16_t*)p);
         p += sizeof(uint16_t);
         SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
         SetGlobalVariablePersistent(sEntry, true);
       } break;
-      case CFX_Value::DataType::STRING: {
+      case CFX_Value::DataType::kString: {
         uint32_t dwLength = *((uint32_t*)p);
         p += sizeof(uint32_t);
         if (p + dwLength > buffer.end())
@@ -344,11 +341,11 @@
         SetGlobalVariablePersistent(sEntry, true);
         p += sizeof(char) * dwLength;
       } break;
-      case CFX_Value::DataType::NULLOBJ: {
+      case CFX_Value::DataType::kNull: {
         SetGlobalVariableNull(sEntry);
         SetGlobalVariablePersistent(sEntry, true);
       } break;
-      case CFX_Value::DataType::OBJECT:
+      case CFX_Value::DataType::kObject:
       default:
         // Arrays aren't allowed in these buffers, nor are unrecoginzed tags.
         return false;
@@ -362,12 +359,12 @@
     return false;
 
   uint32_t nCount = 0;
-  CFX_BinaryBuf sData;
+  BinaryBuffer sData;
   for (const auto& pElement : m_arrayGlobalData) {
     if (!pElement->bPersistent)
       continue;
 
-    CFX_BinaryBuf sElement;
+    BinaryBuffer sElement;
     if (!MakeByteString(pElement->data.sKey, pElement->data, &sElement))
       continue;
 
@@ -378,20 +375,17 @@
     nCount++;
   }
 
-  CFX_BinaryBuf sFile;
-  uint16_t wType = kMagic;
-  uint16_t wVersion = 2;
-  sFile.AppendBlock(&wType, sizeof(uint16_t));
-  sFile.AppendBlock(&wVersion, sizeof(uint16_t));
-  sFile.AppendBlock(&nCount, sizeof(uint32_t));
+  BinaryBuffer sFile;
+  sFile.AppendUint16(kMagic);
+  sFile.AppendUint16(kMaxVersion);
+  sFile.AppendUint32(nCount);
 
-  uint32_t dwSize = sData.GetSize();
-  sFile.AppendBlock(&dwSize, sizeof(uint32_t));
+  uint32_t dwSize = pdfium::base::checked_cast<uint32_t>(sData.GetSize());
+  sFile.AppendUint32(dwSize);
   sFile.AppendSpan(sData.GetSpan());
 
-  CRYPT_ArcFourCryptBlock(sFile.GetSpan(), kRC4KEY);
-
-  return m_pDelegate->StoreBuffer({sFile.GetBuffer(), sFile.GetSize()});
+  CRYPT_ArcFourCryptBlock(sFile.GetMutableSpan(), kRC4KEY);
+  return m_pDelegate->StoreBuffer(sFile.GetSpan());
 }
 
 CFX_GlobalData::Element::Element() = default;
diff --git a/fxjs/cfx_globaldata.h b/fxjs/cfx_globaldata.h
index 421a3b4..6b94265 100644
--- a/fxjs/cfx_globaldata.h
+++ b/fxjs/cfx_globaldata.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,22 +10,20 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/cfx_binarybuf.h"
+#include "core/fxcrt/binary_buffer.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cfx_keyvalue.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/base/span.h"
 
-class CPDFSDK_FormFillEnvironment;
-
 class CFX_GlobalData {
  public:
   class Delegate {
    public:
-    virtual ~Delegate() {}
+    virtual ~Delegate() = default;
 
     virtual bool StoreBuffer(pdfium::span<const uint8_t> pBuffer) = 0;
-    virtual Optional<pdfium::span<uint8_t>> LoadBuffer() = 0;
+    virtual absl::optional<pdfium::span<uint8_t>> LoadBuffer() = 0;
     virtual void BufferDone() = 0;
   };
 
@@ -35,7 +33,7 @@
     ~Element();
 
     CFX_KeyValue data;
-    bool bPersistent;
+    bool bPersistent = false;
   };
 
   static CFX_GlobalData* GetRetainedInstance(Delegate* pDelegate);
@@ -66,16 +64,8 @@
   bool LoadGlobalPersistentVariables();
   bool LoadGlobalPersistentVariablesFromBuffer(pdfium::span<uint8_t> buffer);
   bool SaveGlobalPersisitentVariables();
-
   iterator FindGlobalVariable(const ByteString& sPropname);
 
-  void LoadFileBuffer(const wchar_t* sFilePath,
-                      uint8_t*& pBuffer,
-                      int32_t& nLength);
-  void WriteFileBuffer(const wchar_t* sFilePath,
-                       const char* pBuffer,
-                       int32_t nLength);
-
   size_t m_RefCount = 0;
   UnownedPtr<Delegate> const m_pDelegate;
   std::vector<std::unique_ptr<Element>> m_arrayGlobalData;
diff --git a/fxjs/cfx_globaldata_unittest.cpp b/fxjs/cfx_globaldata_unittest.cpp
index 3eb0ac4..8121701 100644
--- a/fxjs/cfx_globaldata_unittest.cpp
+++ b/fxjs/cfx_globaldata_unittest.cpp
@@ -1,12 +1,15 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/cfx_globaldata.h"
 
+#include <stdint.h>
+
 #include <utility>
 #include <vector>
 
+#include "core/fxcrt/data_vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -14,39 +17,40 @@
 class TestDelegate : public CFX_GlobalData::Delegate {
  public:
   TestDelegate() = default;
-  ~TestDelegate() override {}
+  ~TestDelegate() override = default;
 
   bool StoreBuffer(pdfium::span<const uint8_t> buffer) override {
-    last_buffer_ = std::vector<uint8_t>(buffer.begin(), buffer.end());
+    last_buffer_ = DataVector<uint8_t>(buffer.begin(), buffer.end());
     return true;
   }
-  Optional<pdfium::span<uint8_t>> LoadBuffer() override {
+  absl::optional<pdfium::span<uint8_t>> LoadBuffer() override {
     return pdfium::span<uint8_t>(last_buffer_);
   }
   void BufferDone() override {
-    last_buffer_ = std::vector<uint8_t>();  // Catch misuse after done.
+    // Catch misuse after done.
+    last_buffer_ = DataVector<uint8_t>();
   }
 
-  std::vector<uint8_t> last_buffer_;
+  DataVector<uint8_t> last_buffer_;
 };
 
 }  // namespace
 
 TEST(CFXGlobalData, GetSafety) {
   CFX_GlobalData* pInstance = CFX_GlobalData::GetRetainedInstance(nullptr);
-  EXPECT_EQ(nullptr, pInstance->GetGlobalVariable("nonesuch"));
-  EXPECT_EQ(nullptr, pInstance->GetAt(-1));
-  EXPECT_EQ(nullptr, pInstance->GetAt(0));
-  EXPECT_EQ(nullptr, pInstance->GetAt(1));
+  EXPECT_FALSE(pInstance->GetGlobalVariable("nonesuch"));
+  EXPECT_FALSE(pInstance->GetAt(-1));
+  EXPECT_FALSE(pInstance->GetAt(0));
+  EXPECT_FALSE(pInstance->GetAt(1));
 
   pInstance->SetGlobalVariableNumber("double", 2.0);
   pInstance->SetGlobalVariableString("string", "clams");
 
-  EXPECT_EQ(nullptr, pInstance->GetGlobalVariable("nonesuch"));
-  EXPECT_EQ(nullptr, pInstance->GetAt(-1));
+  EXPECT_FALSE(pInstance->GetGlobalVariable("nonesuch"));
+  EXPECT_FALSE(pInstance->GetAt(-1));
   EXPECT_EQ(pInstance->GetGlobalVariable("double"), pInstance->GetAt(0));
   EXPECT_EQ(pInstance->GetGlobalVariable("string"), pInstance->GetAt(1));
-  EXPECT_EQ(nullptr, pInstance->GetAt(2));
+  EXPECT_FALSE(pInstance->GetAt(2));
 
   ASSERT_TRUE(pInstance->Release());
 }
@@ -71,25 +75,25 @@
   auto* element = pInstance->GetAt(0);
   ASSERT_TRUE(element);
   EXPECT_EQ("double", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::NUMBER, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kNumber, element->data.nType);
   EXPECT_EQ(2.0, element->data.dData);
 
   element = pInstance->GetAt(1);
   ASSERT_TRUE(element);
   EXPECT_EQ("string", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::STRING, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kString, element->data.nType);
   EXPECT_EQ("clams", element->data.sData);
 
   element = pInstance->GetAt(2);
   ASSERT_TRUE(element);
   EXPECT_EQ("boolean", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::BOOLEAN, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kBoolean, element->data.nType);
   EXPECT_EQ(true, element->data.bData);
 
   element = pInstance->GetAt(3);
   ASSERT_TRUE(element);
   EXPECT_EQ("null", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::NULLOBJ, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kNull, element->data.nType);
 
   // Arrays don't get persisted.
   element = pInstance->GetAt(4);
@@ -113,25 +117,25 @@
   auto* element = pInstance->GetAt(0);
   ASSERT_TRUE(element);
   EXPECT_EQ("double", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::NUMBER, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kNumber, element->data.nType);
   EXPECT_EQ(2.0, element->data.dData);
 
   element = pInstance->GetAt(1);
   ASSERT_TRUE(element);
   EXPECT_EQ("string", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::STRING, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kString, element->data.nType);
   EXPECT_EQ("clams", element->data.sData);
 
   element = pInstance->GetAt(2);
   ASSERT_TRUE(element);
   EXPECT_EQ("boolean", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::BOOLEAN, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kBoolean, element->data.nType);
   EXPECT_EQ(true, element->data.bData);
 
   element = pInstance->GetAt(3);
   ASSERT_TRUE(element);
   EXPECT_EQ("null", element->data.sKey);
-  EXPECT_EQ(CFX_Value::DataType::NULLOBJ, element->data.nType);
+  EXPECT_EQ(CFX_Value::DataType::kNull, element->data.nType);
 
   ASSERT_TRUE(pInstance->Release());
 }
diff --git a/fxjs/cfx_keyvalue.cpp b/fxjs/cfx_keyvalue.cpp
index d1c7fae..a0ee7c2 100644
--- a/fxjs/cfx_keyvalue.cpp
+++ b/fxjs/cfx_keyvalue.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/cfx_keyvalue.h b/fxjs/cfx_keyvalue.h
index b66dc12..a6ff52a 100644
--- a/fxjs/cfx_keyvalue.h
+++ b/fxjs/cfx_keyvalue.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,26 +10,26 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 
 class CFX_KeyValue;
 
 class CFX_Value {
  public:
   enum class DataType : uint8_t {
-    NUMBER = 0,
-    BOOLEAN,
-    STRING,
-    OBJECT,
-    NULLOBJ
+    kNumber = 0,
+    kBoolean,
+    kString,
+    kObject,
+    kNull
   };
 
   CFX_Value();
   ~CFX_Value();
 
-  DataType nType = DataType::NULLOBJ;
-  bool bData;
-  double dData;
+  DataType nType = DataType::kNull;
+  bool bData = false;
+  double dData = 0.0;
   ByteString sData;
   std::vector<std::unique_ptr<CFX_KeyValue>> objData;
 };
diff --git a/fxjs/cfx_v8.cpp b/fxjs/cfx_v8.cpp
index 72bf361..422e28a 100644
--- a/fxjs/cfx_v8.cpp
+++ b/fxjs/cfx_v8.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,8 @@
 
 #include "fxjs/cfx_v8.h"
 
-#include "core/fxcrt/fx_memory.h"
-#include "third_party/base/allocator/partition_allocator/partition_alloc.h"
+#include "fxjs/fxv8.h"
+#include "v8/include/v8-isolate.h"
 
 CFX_V8::CFX_V8(v8::Isolate* isolate) : m_pIsolate(isolate) {}
 
@@ -16,103 +16,68 @@
 v8::Local<v8::Value> CFX_V8::GetObjectProperty(
     v8::Local<v8::Object> pObj,
     ByteStringView bsUTF8PropertyName) {
-  if (pObj.IsEmpty())
-    return v8::Local<v8::Value>();
-  v8::Local<v8::Value> val;
-  if (!pObj->Get(m_pIsolate->GetCurrentContext(), NewString(bsUTF8PropertyName))
-           .ToLocal(&val))
-    return v8::Local<v8::Value>();
-  return val;
+  return fxv8::ReentrantGetObjectPropertyHelper(GetIsolate(), pObj,
+                                                bsUTF8PropertyName);
 }
 
 std::vector<WideString> CFX_V8::GetObjectPropertyNames(
     v8::Local<v8::Object> pObj) {
-  if (pObj.IsEmpty())
-    return std::vector<WideString>();
-
-  v8::Local<v8::Array> val;
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  if (!pObj->GetPropertyNames(context).ToLocal(&val))
-    return std::vector<WideString>();
-
-  std::vector<WideString> result;
-  for (uint32_t i = 0; i < val->Length(); ++i) {
-    result.push_back(ToWideString(val->Get(context, i).ToLocalChecked()));
-  }
-
-  return result;
+  return fxv8::ReentrantGetObjectPropertyNamesHelper(GetIsolate(), pObj);
 }
 
-bool CFX_V8::PutObjectProperty(v8::Local<v8::Object> pObj,
+void CFX_V8::PutObjectProperty(v8::Local<v8::Object> pObj,
                                ByteStringView bsUTF8PropertyName,
                                v8::Local<v8::Value> pPut) {
-  ASSERT(!pPut.IsEmpty());
-  if (pObj.IsEmpty())
-    return false;
-
-  v8::Local<v8::String> name = NewString(bsUTF8PropertyName);
-  return pObj->Set(m_pIsolate->GetCurrentContext(), name, pPut).IsJust();
+  fxv8::ReentrantPutObjectPropertyHelper(GetIsolate(), pObj, bsUTF8PropertyName,
+                                         pPut);
 }
 
 void CFX_V8::DisposeIsolate() {
   if (m_pIsolate)
-    m_pIsolate.Release()->Dispose();
+    m_pIsolate.ExtractAsDangling()->Dispose();
 }
 
 v8::Local<v8::Array> CFX_V8::NewArray() {
-  return v8::Array::New(GetIsolate());
+  return fxv8::NewArrayHelper(GetIsolate());
 }
 
 v8::Local<v8::Object> CFX_V8::NewObject() {
-  return v8::Object::New(GetIsolate());
+  return fxv8::NewObjectHelper(GetIsolate());
 }
 
-bool CFX_V8::PutArrayElement(v8::Local<v8::Array> pArray,
-                             unsigned index,
+void CFX_V8::PutArrayElement(v8::Local<v8::Array> pArray,
+                             size_t index,
                              v8::Local<v8::Value> pValue) {
-  ASSERT(!pValue.IsEmpty());
-  if (pArray.IsEmpty())
-    return false;
-  return pArray->Set(m_pIsolate->GetCurrentContext(), index, pValue).IsJust();
+  fxv8::ReentrantPutArrayElementHelper(GetIsolate(), pArray, index, pValue);
 }
 
 v8::Local<v8::Value> CFX_V8::GetArrayElement(v8::Local<v8::Array> pArray,
-                                             unsigned index) {
-  if (pArray.IsEmpty())
-    return v8::Local<v8::Value>();
-  v8::Local<v8::Value> val;
-  if (!pArray->Get(m_pIsolate->GetCurrentContext(), index).ToLocal(&val))
-    return v8::Local<v8::Value>();
-  return val;
+                                             size_t index) {
+  return fxv8::ReentrantGetArrayElementHelper(GetIsolate(), pArray, index);
 }
 
-unsigned CFX_V8::GetArrayLength(v8::Local<v8::Array> pArray) {
-  if (pArray.IsEmpty())
-    return 0;
-  return pArray->Length();
+size_t CFX_V8::GetArrayLength(v8::Local<v8::Array> pArray) {
+  return fxv8::GetArrayLengthHelper(pArray);
 }
 
 v8::Local<v8::Number> CFX_V8::NewNumber(int number) {
-  return v8::Int32::New(GetIsolate(), number);
+  return fxv8::NewNumberHelper(GetIsolate(), number);
 }
 
 v8::Local<v8::Number> CFX_V8::NewNumber(double number) {
-  return v8::Number::New(GetIsolate(), number);
+  return fxv8::NewNumberHelper(GetIsolate(), number);
 }
 
 v8::Local<v8::Number> CFX_V8::NewNumber(float number) {
-  return v8::Number::New(GetIsolate(), number);
+  return fxv8::NewNumberHelper(GetIsolate(), number);
 }
 
 v8::Local<v8::Boolean> CFX_V8::NewBoolean(bool b) {
-  return v8::Boolean::New(GetIsolate(), b);
+  return fxv8::NewBooleanHelper(GetIsolate(), b);
 }
 
 v8::Local<v8::String> CFX_V8::NewString(ByteStringView str) {
-  v8::Isolate* pIsolate = m_pIsolate ? GetIsolate() : v8::Isolate::GetCurrent();
-  return v8::String::NewFromUtf8(pIsolate, str.unterminated_c_str(),
-                                 v8::NewStringType::kNormal, str.GetLength())
-      .ToLocalChecked();
+  return fxv8::NewStringHelper(GetIsolate(), str);
 }
 
 v8::Local<v8::String> CFX_V8::NewString(WideStringView str) {
@@ -123,95 +88,45 @@
 }
 
 v8::Local<v8::Value> CFX_V8::NewNull() {
-  return v8::Null(GetIsolate());
+  return fxv8::NewNullHelper(GetIsolate());
 }
 
 v8::Local<v8::Value> CFX_V8::NewUndefined() {
-  return v8::Undefined(GetIsolate());
+  return fxv8::NewUndefinedHelper(GetIsolate());
 }
 
 v8::Local<v8::Date> CFX_V8::NewDate(double d) {
-  return v8::Date::New(m_pIsolate->GetCurrentContext(), d)
-      .ToLocalChecked()
-      .As<v8::Date>();
+  return fxv8::NewDateHelper(GetIsolate(), d);
 }
 
 int CFX_V8::ToInt32(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return 0;
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  v8::MaybeLocal<v8::Int32> maybe_int32 = pValue->ToInt32(context);
-  if (maybe_int32.IsEmpty())
-    return 0;
-  return maybe_int32.ToLocalChecked()->Value();
+  return fxv8::ReentrantToInt32Helper(GetIsolate(), pValue);
 }
 
 bool CFX_V8::ToBoolean(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return false;
-  return pValue->BooleanValue(m_pIsolate.Get());
+  return fxv8::ReentrantToBooleanHelper(GetIsolate(), pValue);
 }
 
 double CFX_V8::ToDouble(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return 0.0;
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  v8::MaybeLocal<v8::Number> maybe_number = pValue->ToNumber(context);
-  if (maybe_number.IsEmpty())
-    return 0.0;
-  return maybe_number.ToLocalChecked()->Value();
+  return fxv8::ReentrantToDoubleHelper(GetIsolate(), pValue);
 }
 
 WideString CFX_V8::ToWideString(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return WideString();
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  v8::MaybeLocal<v8::String> maybe_string = pValue->ToString(context);
-  if (maybe_string.IsEmpty())
-    return WideString();
-  v8::String::Utf8Value s(GetIsolate(), maybe_string.ToLocalChecked());
-  return WideString::FromUTF8(ByteStringView(*s, s.length()));
+  return fxv8::ReentrantToWideStringHelper(GetIsolate(), pValue);
 }
 
 ByteString CFX_V8::ToByteString(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty())
-    return ByteString();
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  v8::MaybeLocal<v8::String> maybe_string = pValue->ToString(context);
-  if (maybe_string.IsEmpty())
-    return ByteString();
-  v8::String::Utf8Value s(GetIsolate(), maybe_string.ToLocalChecked());
-  return ByteString(*s);
+  return fxv8::ReentrantToByteStringHelper(GetIsolate(), pValue);
 }
 
 v8::Local<v8::Object> CFX_V8::ToObject(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty() || !pValue->IsObject())
-    return v8::Local<v8::Object>();
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  return pValue->ToObject(context).ToLocalChecked();
+  return fxv8::ReentrantToObjectHelper(GetIsolate(), pValue);
 }
 
 v8::Local<v8::Array> CFX_V8::ToArray(v8::Local<v8::Value> pValue) {
-  if (pValue.IsEmpty() || !pValue->IsArray())
-    return v8::Local<v8::Array>();
-  v8::Local<v8::Context> context = m_pIsolate->GetCurrentContext();
-  return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
+  return fxv8::ReentrantToArrayHelper(GetIsolate(), pValue);
 }
 
-void* CFX_V8ArrayBufferAllocator::Allocate(size_t length) {
-  if (length > kMaxAllowedBytes)
-    return nullptr;
-  return GetArrayBufferPartitionAllocator().root()->AllocFlags(
-      pdfium::base::PartitionAllocZeroFill, length, "CFX_V8ArrayBuffer");
-}
-
-void* CFX_V8ArrayBufferAllocator::AllocateUninitialized(size_t length) {
-  if (length > kMaxAllowedBytes)
-    return nullptr;
-  return GetArrayBufferPartitionAllocator().root()->Alloc(length,
-                                                          "CFX_V8ArrayBuffer");
-}
-
-void CFX_V8ArrayBufferAllocator::Free(void* data, size_t length) {
-  GetArrayBufferPartitionAllocator().root()->Free(data);
+void CFX_V8IsolateDeleter::operator()(v8::Isolate* ptr) {
+  ptr->Dispose();
 }
diff --git a/fxjs/cfx_v8.h b/fxjs/cfx_v8.h
index cb152ac..7b2ddc9 100644
--- a/fxjs/cfx_v8.h
+++ b/fxjs/cfx_v8.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,20 @@
 #ifndef FXJS_CFX_V8_H_
 #define FXJS_CFX_V8_H_
 
+#include <stddef.h>
+
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
 
 class CFX_V8 {
  public:
   explicit CFX_V8(v8::Isolate* pIsolate);
   virtual ~CFX_V8();
 
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  v8::Isolate* GetIsolate() const { return m_pIsolate; }
 
   v8::Local<v8::Value> NewNull();
   v8::Local<v8::Value> NewUndefined();
@@ -41,18 +43,18 @@
   v8::Local<v8::Array> ToArray(v8::Local<v8::Value> pValue);
 
   // Arrays.
-  unsigned GetArrayLength(v8::Local<v8::Array> pArray);
+  size_t GetArrayLength(v8::Local<v8::Array> pArray);
   v8::Local<v8::Value> GetArrayElement(v8::Local<v8::Array> pArray,
-                                       unsigned index);
-  bool PutArrayElement(v8::Local<v8::Array> pArray,
-                       unsigned index,
+                                       size_t index);
+  void PutArrayElement(v8::Local<v8::Array> pArray,
+                       size_t index,
                        v8::Local<v8::Value> pValue);
 
   // Objects.
   std::vector<WideString> GetObjectPropertyNames(v8::Local<v8::Object> pObj);
   v8::Local<v8::Value> GetObjectProperty(v8::Local<v8::Object> pObj,
                                          ByteStringView bsUTF8PropertyName);
-  bool PutObjectProperty(v8::Local<v8::Object> pObj,
+  void PutObjectProperty(v8::Local<v8::Object> pObj,
                          ByteStringView bsUTF8PropertyName,
                          v8::Local<v8::Value> pValue);
 
@@ -64,16 +66,9 @@
   UnownedPtr<v8::Isolate> m_pIsolate;
 };
 
-class CFX_V8ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator {
-  static const size_t kMaxAllowedBytes = 0x10000000;
-  void* Allocate(size_t length) override;
-  void* AllocateUninitialized(size_t length) override;
-  void Free(void* data, size_t length) override;
-};
-
 // Use with std::unique_ptr<v8::Isolate> to dispose of isolates correctly.
 struct CFX_V8IsolateDeleter {
-  inline void operator()(v8::Isolate* ptr) { ptr->Dispose(); }
+  void operator()(v8::Isolate* ptr);
 };
 
 #endif  // FXJS_CFX_V8_H_
diff --git a/fxjs/cfx_v8_array_buffer_allocator.cpp b/fxjs/cfx_v8_array_buffer_allocator.cpp
new file mode 100644
index 0000000..c0ddad0
--- /dev/null
+++ b/fxjs/cfx_v8_array_buffer_allocator.cpp
@@ -0,0 +1,53 @@
+// Copyright 2021 The PDFium Authors
+// 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
+
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
+
+#include "core/fxcrt/fx_memory.h"
+
+CFX_V8ArrayBufferAllocator::CFX_V8ArrayBufferAllocator() = default;
+
+CFX_V8ArrayBufferAllocator::~CFX_V8ArrayBufferAllocator() = default;
+
+// NOTE: Under V8 sandbox mode, defer NewDefaultAllocator() call until
+// first use, since V8 must be initialized first for it to succeed, but
+// we need the allocator in order to initialize V8.
+
+void* CFX_V8ArrayBufferAllocator::Allocate(size_t length) {
+  if (length > kMaxAllowedBytes)
+    return nullptr;
+#ifdef V8_ENABLE_SANDBOX
+  if (!wrapped_) {
+    wrapped_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
+  }
+  return wrapped_->Allocate(length);
+#else   // V8_ENABLE_SANDBOX
+  return FX_ArrayBufferAllocate(length);
+#endif  // V8_ENABLE_SANDBOX
+}
+
+void* CFX_V8ArrayBufferAllocator::AllocateUninitialized(size_t length) {
+  if (length > kMaxAllowedBytes)
+    return nullptr;
+#ifdef V8_ENABLE_SANDBOX
+  if (!wrapped_) {
+    wrapped_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
+  }
+  return wrapped_->AllocateUninitialized(length);
+#else  // V8_ENABLE_SANDBOX
+  return FX_ArrayBufferAllocateUninitialized(length);
+#endif
+}
+
+void CFX_V8ArrayBufferAllocator::Free(void* data, size_t length) {
+#ifdef V8_ENABLE_SANDBOX
+  if (wrapped_) {
+    wrapped_->Free(data, length);
+  }
+#else  // V8_ENABLE_SANDBOX
+  FX_ArrayBufferFree(data);
+#endif
+}
diff --git a/fxjs/cfx_v8_array_buffer_allocator.h b/fxjs/cfx_v8_array_buffer_allocator.h
new file mode 100644
index 0000000..a6daef0
--- /dev/null
+++ b/fxjs/cfx_v8_array_buffer_allocator.h
@@ -0,0 +1,35 @@
+// Copyright 2021 The PDFium Authors
+// 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 FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_
+#define FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "v8/include/v8-array-buffer.h"
+
+class CFX_V8ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator {
+ public:
+  static const size_t kMaxAllowedBytes = 0x10000000;
+
+  CFX_V8ArrayBufferAllocator();
+  CFX_V8ArrayBufferAllocator(const CFX_V8ArrayBufferAllocator&) = delete;
+  CFX_V8ArrayBufferAllocator(CFX_V8ArrayBufferAllocator&&) = delete;
+  ~CFX_V8ArrayBufferAllocator() override;
+
+  void* Allocate(size_t length) override;
+  void* AllocateUninitialized(size_t length) override;
+  void Free(void* data, size_t length) override;
+
+#ifdef V8_ENABLE_SANDBOX
+ private:
+  std::unique_ptr<v8::ArrayBuffer::Allocator> wrapped_;
+#endif  // V8_ENABLE_SANDBOX
+};
+
+#endif  // FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_
diff --git a/fxjs/cfx_v8_unittest.cpp b/fxjs/cfx_v8_unittest.cpp
index 7b9ecb5..0a2dca8 100644
--- a/fxjs/cfx_v8_unittest.cpp
+++ b/fxjs/cfx_v8_unittest.cpp
@@ -1,39 +1,43 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fxjs/cfx_v8_unittest.h"
+#include "fxjs/cfx_v8.h"
+
+#include <math.h>
 
 #include <memory>
 
-#include "fxjs/cfx_v8.h"
+#include "testing/fxv8_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-date.h"
+#include "v8/include/v8-isolate.h"
 
 namespace {
 bool getter_sentinel = false;
 bool setter_sentinel = false;
 }  // namespace
 
-void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const {
-  ptr->Dispose();
-}
+class CFXV8UnitTest : public FXV8UnitTest {
+ public:
+  CFXV8UnitTest() = default;
+  ~CFXV8UnitTest() override = default;
 
-FXV8UnitTest::FXV8UnitTest() = default;
+  // FXV8UnitTest:
+  void SetUp() override {
+    FXV8UnitTest::SetUp();
+    cfx_v8_ = std::make_unique<CFX_V8>(isolate());
+  }
 
-FXV8UnitTest::~FXV8UnitTest() = default;
+  CFX_V8* cfx_v8() const { return cfx_v8_.get(); }
 
-void FXV8UnitTest::SetUp() {
-  array_buffer_allocator_ = pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>();
+ protected:
+  std::unique_ptr<CFX_V8> cfx_v8_;
+};
 
-  v8::Isolate::CreateParams params;
-  params.array_buffer_allocator = array_buffer_allocator_.get();
-  isolate_.reset(v8::Isolate::New(params));
-
-  cfx_v8_ = pdfium::MakeUnique<CFX_V8>(isolate_.get());
-}
-
-TEST_F(FXV8UnitTest, EmptyLocal) {
+TEST_F(CFXV8UnitTest, EmptyLocal) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -50,18 +54,18 @@
   // Can't set properties on empty objects, but does not fault.
   v8::Local<v8::Value> marker = cfx_v8()->NewNumber(2);
   v8::Local<v8::Object> empty_object;
-  EXPECT_FALSE(cfx_v8()->PutObjectProperty(empty_object, "clams", marker));
+  cfx_v8()->PutObjectProperty(empty_object, "clams", marker);
   EXPECT_TRUE(cfx_v8()->GetObjectProperty(empty_object, "clams").IsEmpty());
   EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(empty_object).size());
 
   // Can't set elements in empty arrays, but does not fault.
   v8::Local<v8::Array> empty_array;
-  EXPECT_FALSE(cfx_v8()->PutArrayElement(empty_array, 0, marker));
+  cfx_v8()->PutArrayElement(empty_array, 0, marker);
   EXPECT_TRUE(cfx_v8()->GetArrayElement(empty_array, 0).IsEmpty());
   EXPECT_EQ(0u, cfx_v8()->GetArrayLength(empty_array));
 }
 
-TEST_F(FXV8UnitTest, NewNull) {
+TEST_F(CFXV8UnitTest, NewNull) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -76,7 +80,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(nullz).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewUndefined) {
+TEST_F(CFXV8UnitTest, NewUndefined) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -84,14 +88,14 @@
   auto undef = cfx_v8()->NewUndefined();
   EXPECT_FALSE(cfx_v8()->ToBoolean(undef));
   EXPECT_EQ(0, cfx_v8()->ToInt32(undef));
-  EXPECT_TRUE(std::isnan(cfx_v8()->ToDouble(undef)));
+  EXPECT_TRUE(isnan(cfx_v8()->ToDouble(undef)));
   EXPECT_EQ("undefined", cfx_v8()->ToByteString(undef));
   EXPECT_EQ(L"undefined", cfx_v8()->ToWideString(undef));
   EXPECT_TRUE(cfx_v8()->ToObject(undef).IsEmpty());
   EXPECT_TRUE(cfx_v8()->ToArray(undef).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewBoolean) {
+TEST_F(CFXV8UnitTest, NewBoolean) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -115,7 +119,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(boolz).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewNumber) {
+TEST_F(CFXV8UnitTest, NewNumber) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -130,7 +134,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(num).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewString) {
+TEST_F(CFXV8UnitTest, NewString) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -154,7 +158,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(str2).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewDate) {
+TEST_F(CFXV8UnitTest, NewDate) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -169,7 +173,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(date).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, NewArray) {
+TEST_F(CFXV8UnitTest, NewArray) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -180,7 +184,7 @@
   EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
   EXPECT_EQ(0u, cfx_v8()->GetArrayLength(array));
 
-  EXPECT_TRUE(cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12)));
+  cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12));
   EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 2).IsEmpty());
   EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
   EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 3).IsEmpty());
@@ -196,7 +200,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(array)->IsArray());
 }
 
-TEST_F(FXV8UnitTest, NewObject) {
+TEST_F(CFXV8UnitTest, NewObject) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(v8::Context::New(isolate()));
@@ -208,8 +212,7 @@
   EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsUndefined());
   EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(object).size());
 
-  EXPECT_TRUE(
-      cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12)));
+  cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12));
   EXPECT_FALSE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
   EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsNumber());
   EXPECT_EQ(1u, cfx_v8()->GetObjectPropertyNames(object).size());
@@ -224,7 +227,7 @@
   EXPECT_TRUE(cfx_v8()->ToArray(object).IsEmpty());
 }
 
-TEST_F(FXV8UnitTest, ThrowFromGetter) {
+TEST_F(CFXV8UnitTest, ThrowFromGetter) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Local<v8::Context> context = v8::Context::New(isolate());
@@ -246,7 +249,7 @@
   EXPECT_TRUE(getter_sentinel);
 }
 
-TEST_F(FXV8UnitTest, ThrowFromSetter) {
+TEST_F(CFXV8UnitTest, ThrowFromSetter) {
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
   v8::Local<v8::Context> context = v8::Context::New(isolate());
@@ -264,6 +267,6 @@
                                 })
                   .FromJust());
   setter_sentinel = false;
-  EXPECT_FALSE(cfx_v8()->PutObjectProperty(object, "clams", name));
+  cfx_v8()->PutObjectProperty(object, "clams", name);
   EXPECT_TRUE(setter_sentinel);
 }
diff --git a/fxjs/cfx_v8_unittest.h b/fxjs/cfx_v8_unittest.h
deleted file mode 100644
index e5d4e3f..0000000
--- a/fxjs/cfx_v8_unittest.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef FXJS_CFX_V8_UNITTEST_H_
-#define FXJS_CFX_V8_UNITTEST_H_
-
-#include <memory>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-class CFX_V8;
-class CFX_V8ArrayBufferAllocator;
-
-namespace v8 {
-class Isolate;
-}  // namespace v8
-
-class FXV8UnitTest : public ::testing::Test {
- public:
-  struct V8IsolateDeleter {
-    void operator()(v8::Isolate* ptr) const;
-  };
-
-  FXV8UnitTest();
-  ~FXV8UnitTest() override;
-
-  void SetUp() override;
-
-  v8::Isolate* isolate() const { return isolate_.get(); }
-  CFX_V8* cfx_v8() const { return cfx_v8_.get(); }
-
- protected:
-  std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
-  std::unique_ptr<v8::Isolate, V8IsolateDeleter> isolate_;
-  std::unique_ptr<CFX_V8> cfx_v8_;
-};
-
-#endif  // FXJS_CFX_V8_UNITTEST_H_
diff --git a/fxjs/cfxjs_engine.cpp b/fxjs/cfxjs_engine.cpp
index c4d46a2..35653c9 100644
--- a/fxjs/cfxjs_engine.cpp
+++ b/fxjs/cfxjs_engine.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,17 +8,23 @@
 
 #include <memory>
 #include <utility>
-#include <vector>
 
+#include "core/fxcrt/stl_util.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
 #include "fxjs/cjs_object.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_runtimedata.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-message.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-script.h"
 #include "v8/include/v8-util.h"
 
-class CFXJS_PerObjectData;
-
 namespace {
 
 unsigned int g_embedderDataSlot = 1u;
@@ -26,7 +32,14 @@
 size_t g_isolate_ref_count = 0;
 CFX_V8ArrayBufferAllocator* g_arrayBufferAllocator = nullptr;
 v8::Global<v8::ObjectTemplate>* g_DefaultGlobalObjectTemplate = nullptr;
+
+// Only the address matters, values are for humans debugging. ASLR should
+// ensure that these values are unlikely to arise otherwise. Keep these
+// wchar_t to prevent the compiler from doing something clever, like
+// aligning them on a byte boundary to save space, which would make them
+// incompatible for use as V8 aligned pointers.
 const wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData";
+const wchar_t kPerIsolateDataTag[] = L"FXJS_PerIsolateData";
 
 void* GetAlignedPointerForPerObjectDataTag() {
   return const_cast<void*>(static_cast<const void*>(kPerObjectDataTag));
@@ -42,6 +55,41 @@
 
 }  // namespace
 
+class CFXJS_PerObjectData {
+ public:
+  ~CFXJS_PerObjectData() = default;
+
+  static void SetNewDataInObject(uint32_t nObjDefnID,
+                                 v8::Local<v8::Object> pObj) {
+    if (pObj->InternalFieldCount() == 2) {
+      pObj->SetAlignedPointerInInternalField(
+          0, GetAlignedPointerForPerObjectDataTag());
+      pObj->SetAlignedPointerInInternalField(
+          1, new CFXJS_PerObjectData(nObjDefnID));
+    }
+  }
+
+  static CFXJS_PerObjectData* GetFromObject(v8::Local<v8::Object> pObj) {
+    if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 ||
+        pObj->GetAlignedPointerFromInternalField(0) !=
+            GetAlignedPointerForPerObjectDataTag()) {
+      return nullptr;
+    }
+    return static_cast<CFXJS_PerObjectData*>(
+        pObj->GetAlignedPointerFromInternalField(1));
+  }
+
+  uint32_t GetObjDefnID() const { return m_ObjDefnID; }
+  CJS_Object* GetPrivate() { return m_pPrivate.get(); }
+  void SetPrivate(std::unique_ptr<CJS_Object> p) { m_pPrivate = std::move(p); }
+
+ private:
+  explicit CFXJS_PerObjectData(uint32_t nObjDefnID) : m_ObjDefnID(nObjDefnID) {}
+
+  const uint32_t m_ObjDefnID;
+  std::unique_ptr<CJS_Object> m_pPrivate;
+};
+
 // Global weak map to save dynamic objects.
 class V8TemplateMapTraits final
     : public v8::StdMapTraits<CFXJS_PerObjectData*, v8::Object> {
@@ -84,8 +132,9 @@
   explicit V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {}
   ~V8TemplateMap() = default;
 
-  void SetAndMakeWeak(WeakCallbackDataType* key, v8::Local<v8::Object> handle) {
-    ASSERT(!m_map.Contains(key));
+  void SetAndMakeWeak(v8::Local<v8::Object> handle) {
+    WeakCallbackDataType* key = CFXJS_PerObjectData::GetFromObject(handle);
+    DCHECK(!m_map.Contains(key));
 
     // Inserting an object into a GlobalValueMap with the appropriate traits
     // has the side-effect of making the object weak deep in the guts of V8,
@@ -93,41 +142,12 @@
     m_map.Set(key, handle);
   }
 
-  friend class V8TemplateMapTraits;
+  MapType* GetMap() { return &m_map; }
 
  private:
   MapType m_map;
 };
 
-class CFXJS_PerObjectData {
- public:
-  explicit CFXJS_PerObjectData(int nObjDefID) : m_ObjDefID(nObjDefID) {}
-
-  ~CFXJS_PerObjectData() = default;
-
-  static void SetInObject(CFXJS_PerObjectData* pData,
-                          v8::Local<v8::Object> pObj) {
-    if (pObj->InternalFieldCount() == 2) {
-      pObj->SetAlignedPointerInInternalField(
-          0, GetAlignedPointerForPerObjectDataTag());
-      pObj->SetAlignedPointerInInternalField(1, pData);
-    }
-  }
-
-  static CFXJS_PerObjectData* GetFromObject(v8::Local<v8::Object> pObj) {
-    if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 ||
-        pObj->GetAlignedPointerFromInternalField(0) !=
-            GetAlignedPointerForPerObjectDataTag()) {
-      return nullptr;
-    }
-    return static_cast<CFXJS_PerObjectData*>(
-        pObj->GetAlignedPointerFromInternalField(1));
-  }
-
-  const int m_ObjDefID;
-  std::unique_ptr<CJS_Object> m_pPrivate;
-};
-
 class CFXJS_ObjDefinition {
  public:
   CFXJS_ObjDefinition(v8::Isolate* isolate,
@@ -142,42 +162,37 @@
         m_pIsolate(isolate) {
     v8::Isolate::Scope isolate_scope(isolate);
     v8::HandleScope handle_scope(isolate);
-    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
-    fun->InstanceTemplate()->SetInternalFieldCount(2);
-    fun->SetCallHandler(CallHandler, v8::Number::New(isolate, eObjType));
+    v8::Local<v8::FunctionTemplate> fn = v8::FunctionTemplate::New(isolate);
+    fn->InstanceTemplate()->SetInternalFieldCount(2);
+    fn->InstanceTemplate()->SetImmutableProto();
+    fn->SetCallHandler(CallHandler, v8::Number::New(isolate, eObjType));
     if (eObjType == FXJSOBJTYPE_GLOBAL) {
-      fun->InstanceTemplate()->Set(
-          v8::Symbol::GetToStringTag(isolate),
-          v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal)
-              .ToLocalChecked());
+      fn->InstanceTemplate()->Set(v8::Symbol::GetToStringTag(isolate),
+                                  fxv8::NewStringHelper(isolate, "global"));
     }
-    m_FunctionTemplate.Reset(isolate, fun);
-    m_Signature.Reset(isolate, v8::Signature::New(isolate, fun));
+    m_FunctionTemplate.Reset(isolate, fn);
+    m_Signature.Reset(isolate, v8::Signature::New(isolate, fn));
   }
 
   static void CallHandler(const v8::FunctionCallbackInfo<v8::Value>& info) {
     v8::Isolate* isolate = info.GetIsolate();
     if (!info.IsConstructCall()) {
-      isolate->ThrowException(
-          v8::String::NewFromUtf8(isolate, "illegal constructor",
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked());
+      fxv8::ThrowExceptionHelper(isolate, "illegal constructor");
       return;
     }
     if (info.Data().As<v8::Int32>()->Value() != FXJSOBJTYPE_DYNAMIC) {
-      isolate->ThrowException(
-          v8::String::NewFromUtf8(isolate, "not a dynamic object",
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked());
+      fxv8::ThrowExceptionHelper(isolate, "not a dynamic object");
       return;
     }
     v8::Local<v8::Object> holder = info.Holder();
-    ASSERT(holder->InternalFieldCount() == 2);
+    DCHECK_EQ(holder->InternalFieldCount(), 2);
     holder->SetAlignedPointerInInternalField(0, nullptr);
     holder->SetAlignedPointerInInternalField(1, nullptr);
   }
 
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  FXJSOBJTYPE GetObjType() const { return m_ObjType; }
+  const char* GetObjName() const { return m_ObjName; }
+  v8::Isolate* GetIsolate() const { return m_pIsolate; }
 
   void DefineConst(const char* sConstName, v8::Local<v8::Value> pDefault) {
     GetInstanceTemplate()->Set(GetIsolate(), sConstName, pDefault);
@@ -197,12 +212,14 @@
     GetInstanceTemplate()->Set(sMethodName, fun, v8::ReadOnly);
   }
 
-  void DefineAllProperties(v8::GenericNamedPropertyQueryCallback pPropQurey,
-                           v8::GenericNamedPropertyGetterCallback pPropGet,
-                           v8::GenericNamedPropertySetterCallback pPropPut,
-                           v8::GenericNamedPropertyDeleterCallback pPropDel) {
+  void DefineAllProperties(
+      v8::GenericNamedPropertyQueryCallback pPropQurey,
+      v8::GenericNamedPropertyGetterCallback pPropGet,
+      v8::GenericNamedPropertySetterCallback pPropPut,
+      v8::GenericNamedPropertyDeleterCallback pPropDel,
+      v8::GenericNamedPropertyEnumeratorCallback pPropEnum) {
     GetInstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
-        pPropGet, pPropPut, pPropQurey, pPropDel, nullptr,
+        pPropGet, pPropPut, pPropQurey, pPropDel, pPropEnum,
         v8::Local<v8::Value>(),
         v8::PropertyHandlerFlags::kOnlyInterceptStrings));
   }
@@ -219,7 +236,20 @@
     return scope.Escape(m_Signature.Get(GetIsolate()));
   }
 
-  const char* const m_ObjName;
+  void RunConstructor(CFXJS_Engine* pEngine,
+                      v8::Local<v8::Object> obj,
+                      v8::Local<v8::Object> proxy) {
+    if (m_pConstructor)
+      m_pConstructor(pEngine, obj, proxy);
+  }
+
+  void RunDestructor(v8::Local<v8::Object> obj) {
+    if (m_pDestructor)
+      m_pDestructor(obj);
+  }
+
+ private:
+  UnownedPtr<const char> const m_ObjName;
   const FXJSOBJTYPE m_ObjType;
   const CFXJS_Engine::Constructor m_pConstructor;
   const CFXJS_Engine::Destructor m_pDestructor;
@@ -231,18 +261,16 @@
 static v8::Local<v8::ObjectTemplate> GetGlobalObjectTemplate(
     v8::Isolate* pIsolate) {
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(pIsolate);
-  for (int i = 0; i < pIsolateData->MaxObjDefinitionID(); ++i) {
+  for (uint32_t i = 1; i <= pIsolateData->CurrentMaxObjDefinitionID(); ++i) {
     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL)
+    if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL)
       return pObjDef->GetInstanceTemplate();
   }
   if (!g_DefaultGlobalObjectTemplate) {
     v8::Local<v8::ObjectTemplate> hGlobalTemplate =
         v8::ObjectTemplate::New(pIsolate);
-    hGlobalTemplate->Set(
-        v8::Symbol::GetToStringTag(pIsolate),
-        v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-            .ToLocalChecked());
+    hGlobalTemplate->Set(v8::Symbol::GetToStringTag(pIsolate),
+                         fxv8::NewStringHelper(pIsolate, "global"));
     g_DefaultGlobalObjectTemplate =
         new v8::Global<v8::ObjectTemplate>(pIsolate, hGlobalTemplate);
   }
@@ -255,15 +283,14 @@
   v8::Local<v8::Object> obj = value.Get(isolate);
   if (obj.IsEmpty())
     return;
-  int id = CFXJS_Engine::GetObjDefnID(obj);
-  if (id == -1)
+  uint32_t id = CFXJS_Engine::GetObjDefnID(obj);
+  if (id == 0)
     return;
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(isolate);
   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(id);
   if (!pObjDef)
     return;
-  if (pObjDef->m_pDestructor)
-    pObjDef->m_pDestructor(obj);
+  pObjDef->RunDestructor(obj);
   CFXJS_Engine::FreeObjectPrivate(obj);
 }
 
@@ -273,16 +300,16 @@
 }
 
 V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo(
-    const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
-  V8TemplateMap* pMap =
-      FXJS_PerIsolateData::Get(data.GetIsolate())->m_pDynamicObjsMap.get();
-  return pMap ? &pMap->m_map : nullptr;
+    const v8::WeakCallbackInfo<WeakCallbackDataType>& info) {
+  auto* pIsolateData = FXJS_PerIsolateData::Get(info.GetIsolate());
+  V8TemplateMap* pObjsMap = pIsolateData->GetDynamicObjsMap();
+  return pObjsMap ? pObjsMap->GetMap() : nullptr;
 }
 
 void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) {
   if (g_isolate) {
-    ASSERT(g_embedderDataSlot == embedderDataSlot);
-    ASSERT(g_isolate == pIsolate);
+    DCHECK_EQ(g_embedderDataSlot, embedderDataSlot);
+    DCHECK_EQ(g_isolate, pIsolate);
     return;
   }
   g_embedderDataSlot = embedderDataSlot;
@@ -290,7 +317,7 @@
 }
 
 void FXJS_Release() {
-  ASSERT(!g_isolate || g_isolate_ref_count == 0);
+  DCHECK(!g_isolate || g_isolate_ref_count == 0);
   delete g_DefaultGlobalObjectTemplate;
   g_DefaultGlobalObjectTemplate = nullptr;
   g_isolate = nullptr;
@@ -317,8 +344,6 @@
   return g_isolate_ref_count;
 }
 
-FXJS_PerIsolateData::~FXJS_PerIsolateData() {}
-
 // static
 void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) {
   if (!pIsolate->GetData(g_embedderDataSlot))
@@ -327,26 +352,33 @@
 
 // static
 FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
-  return static_cast<FXJS_PerIsolateData*>(
-      pIsolate->GetData(g_embedderDataSlot));
-}
-
-int FXJS_PerIsolateData::MaxObjDefinitionID() const {
-  return pdfium::CollectionSize<int>(m_ObjectDefnArray);
+  auto* result =
+      static_cast<FXJS_PerIsolateData*>(pIsolate->GetData(g_embedderDataSlot));
+  CHECK(result->m_Tag == kPerIsolateDataTag);
+  return result;
 }
 
 FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate)
-    : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {}
+    : m_Tag(kPerIsolateDataTag),
+      m_pDynamicObjsMap(std::make_unique<V8TemplateMap>(pIsolate)) {}
 
-CFXJS_ObjDefinition* FXJS_PerIsolateData::ObjDefinitionForID(int id) const {
-  return (id >= 0 && id < MaxObjDefinitionID()) ? m_ObjectDefnArray[id].get()
-                                                : nullptr;
+FXJS_PerIsolateData::~FXJS_PerIsolateData() = default;
+
+uint32_t FXJS_PerIsolateData::CurrentMaxObjDefinitionID() const {
+  return fxcrt::CollectionSize<uint32_t>(m_ObjectDefnArray);
 }
 
-int FXJS_PerIsolateData::AssignIDForObjDefinition(
+CFXJS_ObjDefinition* FXJS_PerIsolateData::ObjDefinitionForID(
+    uint32_t id) const {
+  return id > 0 && id <= CurrentMaxObjDefinitionID()
+             ? m_ObjectDefnArray[id - 1].get()
+             : nullptr;
+}
+
+uint32_t FXJS_PerIsolateData::AssignIDForObjDefinition(
     std::unique_ptr<CFXJS_ObjDefinition> pDefn) {
   m_ObjectDefnArray.push_back(std::move(pDefn));
-  return m_ObjectDefnArray.size() - 1;
+  return CurrentMaxObjDefinitionID();
 }
 
 CFXJS_Engine::CFXJS_Engine() : CFX_V8(nullptr) {}
@@ -356,9 +388,9 @@
 CFXJS_Engine::~CFXJS_Engine() = default;
 
 // static
-int CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) {
+uint32_t CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) {
   CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
-  return pData ? pData->m_ObjDefID : -1;
+  return pData ? pData->GetObjDefnID() : 0;
 }
 
 // static
@@ -366,9 +398,8 @@
                                     std::unique_ptr<CJS_Object> p) {
   CFXJS_PerObjectData* pPerObjectData =
       CFXJS_PerObjectData::GetFromObject(pObj);
-  if (!pPerObjectData)
-    return;
-  pPerObjectData->m_pPrivate = std::move(p);
+  if (pPerObjectData)
+    pPerObjectData->SetPrivate(std::move(p));
 }
 
 // static
@@ -379,20 +410,20 @@
   delete pData;
 }
 
-int CFXJS_Engine::DefineObj(const char* sObjName,
-                            FXJSOBJTYPE eObjType,
-                            CFXJS_Engine::Constructor pConstructor,
-                            CFXJS_Engine::Destructor pDestructor) {
+uint32_t CFXJS_Engine::DefineObj(const char* sObjName,
+                                 FXJSOBJTYPE eObjType,
+                                 CFXJS_Engine::Constructor pConstructor,
+                                 CFXJS_Engine::Destructor pDestructor) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::HandleScope handle_scope(GetIsolate());
   FXJS_PerIsolateData::SetUp(GetIsolate());
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
   return pIsolateData->AssignIDForObjDefinition(
-      pdfium::MakeUnique<CFXJS_ObjDefinition>(GetIsolate(), sObjName, eObjType,
-                                              pConstructor, pDestructor));
+      std::make_unique<CFXJS_ObjDefinition>(GetIsolate(), sObjName, eObjType,
+                                            pConstructor, pDestructor));
 }
 
-void CFXJS_Engine::DefineObjMethod(int nObjDefnID,
+void CFXJS_Engine::DefineObjMethod(uint32_t nObjDefnID,
                                    const char* sMethodName,
                                    v8::FunctionCallback pMethodCall) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
@@ -402,7 +433,7 @@
   pObjDef->DefineMethod(NewString(sMethodName), pMethodCall);
 }
 
-void CFXJS_Engine::DefineObjProperty(int nObjDefnID,
+void CFXJS_Engine::DefineObjProperty(uint32_t nObjDefnID,
                                      const char* sPropName,
                                      v8::AccessorGetterCallback pPropGet,
                                      v8::AccessorSetterCallback pPropPut) {
@@ -414,19 +445,21 @@
 }
 
 void CFXJS_Engine::DefineObjAllProperties(
-    int nObjDefnID,
+    uint32_t nObjDefnID,
     v8::GenericNamedPropertyQueryCallback pPropQurey,
     v8::GenericNamedPropertyGetterCallback pPropGet,
     v8::GenericNamedPropertySetterCallback pPropPut,
-    v8::GenericNamedPropertyDeleterCallback pPropDel) {
+    v8::GenericNamedPropertyDeleterCallback pPropDel,
+    v8::GenericNamedPropertyEnumeratorCallback pPropEnum) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::HandleScope handle_scope(GetIsolate());
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
   CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID);
-  pObjDef->DefineAllProperties(pPropQurey, pPropGet, pPropPut, pPropDel);
+  pObjDef->DefineAllProperties(pPropQurey, pPropGet, pPropPut, pPropDel,
+                               pPropEnum);
 }
 
-void CFXJS_Engine::DefineObjConst(int nObjDefnID,
+void CFXJS_Engine::DefineObjConst(uint32_t nObjDefnID,
                                   const char* sConstName,
                                   v8::Local<v8::Value> pDefault) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
@@ -486,24 +519,15 @@
 
   v8::Context::Scope context_scope(v8Context);
   FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
-  int maxID = pIsolateData->MaxObjDefinitionID();
+  uint32_t maxID = pIsolateData->CurrentMaxObjDefinitionID();
   m_StaticObjects.resize(maxID + 1);
-  for (int i = 0; i < maxID; ++i) {
+  for (uint32_t i = 1; i <= maxID; ++i) {
     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
-      CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i),
-                                       v8Context->Global()
-                                           ->GetPrototype()
-                                           ->ToObject(v8Context)
-                                           .ToLocalChecked());
-      if (pObjDef->m_pConstructor) {
-        pObjDef->m_pConstructor(this, v8Context->Global()
-                                          ->GetPrototype()
-                                          ->ToObject(v8Context)
-                                          .ToLocalChecked());
-      }
-    } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) {
-      v8::Local<v8::String> pObjName = NewString(pObjDef->m_ObjName);
+    if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) {
+      CFXJS_PerObjectData::SetNewDataInObject(i, pThis);
+      pObjDef->RunConstructor(this, pThis, pThisProxy);
+    } else if (pObjDef->GetObjType() == FXJSOBJTYPE_STATIC) {
+      v8::Local<v8::String> pObjName = NewString(pObjDef->GetObjName());
       v8::Local<v8::Object> obj = NewFXJSBoundObject(i, FXJSOBJTYPE_STATIC);
       if (!obj.IsEmpty()) {
         v8Context->Global()->Set(v8Context, pObjName, obj).FromJust();
@@ -525,10 +549,10 @@
 
   m_ConstArrays.clear();
 
-  for (int i = 0; i < pIsolateData->MaxObjDefinitionID(); ++i) {
+  for (uint32_t i = 1; i <= pIsolateData->CurrentMaxObjDefinitionID(); ++i) {
     CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i);
     v8::Local<v8::Object> pObj;
-    if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
+    if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) {
       pObj =
           context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
     } else if (!m_StaticObjects[i].IsEmpty()) {
@@ -536,8 +560,7 @@
       m_StaticObjects[i].Reset();
     }
     if (!pObj.IsEmpty()) {
-      if (pObjDef->m_pDestructor)
-        pObjDef->m_pDestructor(pObj);
+      pObjDef->RunDestructor(pObj);
       FreeObjectPrivate(pObj);
     }
   }
@@ -551,7 +574,7 @@
   GetIsolate()->SetData(g_embedderDataSlot, nullptr);
 }
 
-Optional<IJS_Runtime::JS_Error> CFXJS_Engine::Execute(
+absl::optional<IJS_Runtime::JS_Error> CFXJS_Engine::Execute(
     const WideString& script) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::TryCatch try_catch(GetIsolate());
@@ -576,10 +599,10 @@
     std::tie(line, column) = GetLineAndColumnFromError(msg, context);
     return IJS_Runtime::JS_Error(line, column, WideString::FromUTF8(*error));
   }
-  return pdfium::nullopt;
+  return absl::nullopt;
 }
 
-v8::Local<v8::Object> CFXJS_Engine::NewFXJSBoundObject(int nObjDefnID,
+v8::Local<v8::Object> CFXJS_Engine::NewFXJSBoundObject(uint32_t nObjDefnID,
                                                        FXJSOBJTYPE type) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
@@ -595,15 +618,13 @@
   if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj))
     return v8::Local<v8::Object>();
 
-  CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID);
-  CFXJS_PerObjectData::SetInObject(pObjData, obj);
-  if (pObjDef->m_pConstructor)
-    pObjDef->m_pConstructor(this, obj);
-
+  CFXJS_PerObjectData::SetNewDataInObject(nObjDefnID, obj);
+  pObjDef->RunConstructor(this, obj, obj);
   if (type == FXJSOBJTYPE_DYNAMIC) {
     auto* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate());
-    if (pIsolateData->m_pDynamicObjsMap)
-      pIsolateData->m_pDynamicObjsMap->SetAndMakeWeak(pObjData, obj);
+    V8TemplateMap* pObjsMap = pIsolateData->GetDynamicObjsMap();
+    if (pObjsMap)
+      pObjsMap->SetAndMakeWeak(obj);
   }
   return obj;
 }
@@ -619,7 +640,7 @@
 }
 
 void CFXJS_Engine::Error(const WideString& message) {
-  GetIsolate()->ThrowException(NewString(message.AsStringView()));
+  fxv8::ThrowExceptionHelper(GetIsolate(), message.AsStringView());
 }
 
 v8::Local<v8::Context> CFXJS_Engine::GetV8Context() {
@@ -627,10 +648,11 @@
 }
 
 // static
-CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Local<v8::Object> pObj) {
+CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Object> pObj) {
   auto* pData = CFXJS_PerObjectData::GetFromObject(pObj);
   if (pData)
-    return pData->m_pPrivate.get();
+    return pData->GetPrivate();
 
   if (pObj.IsEmpty())
     return nullptr;
@@ -645,16 +667,16 @@
   if (!pProtoData)
     return nullptr;
 
-  auto* pIsolateData = FXJS_PerIsolateData::Get(v8::Isolate::GetCurrent());
+  auto* pIsolateData = FXJS_PerIsolateData::Get(pIsolate);
   if (!pIsolateData)
     return nullptr;
 
   CFXJS_ObjDefinition* pObjDef =
-      pIsolateData->ObjDefinitionForID(pProtoData->m_ObjDefID);
-  if (!pObjDef || pObjDef->m_ObjType != FXJSOBJTYPE_GLOBAL)
+      pIsolateData->ObjDefinitionForID(pProtoData->GetObjDefnID());
+  if (!pObjDef || pObjDef->GetObjType() != FXJSOBJTYPE_GLOBAL)
     return nullptr;
 
-  return pProtoData->m_pPrivate.get();
+  return pProtoData->GetPrivate();
 }
 
 v8::Local<v8::Array> CFXJS_Engine::GetConstArray(const WideString& name) {
diff --git a/fxjs/cfxjs_engine.h b/fxjs/cfxjs_engine.h
index 933c250..b0011fc 100644
--- a/fxjs/cfxjs_engine.h
+++ b/fxjs/cfxjs_engine.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,21 +17,21 @@
 #include <functional>
 #include <map>
 #include <memory>
+#include <utility>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 #include "fxjs/cfx_v8.h"
 #include "fxjs/ijs_runtime.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-persistent-handle.h"
+#include "v8/include/v8-template.h"
 
 class CFXJS_ObjDefinition;
 class CJS_Object;
 class V8TemplateMap;
 
-// CFXJS_ENGINE places no restrictions on this class; it merely passes it
-// on to caller-provided methods.
-class IJS_EventContext;  // A description of the event that caused JS execution.
-
 enum FXJSOBJTYPE {
   FXJSOBJTYPE_DYNAMIC = 0,  // Created by native method and returned to JS.
   FXJSOBJTYPE_STATIC,       // Created by init and hung off of global object.
@@ -51,16 +51,22 @@
   static void SetUp(v8::Isolate* pIsolate);
   static FXJS_PerIsolateData* Get(v8::Isolate* pIsolate);
 
-  int MaxObjDefinitionID() const;
-  CFXJS_ObjDefinition* ObjDefinitionForID(int id) const;
-  int AssignIDForObjDefinition(std::unique_ptr<CFXJS_ObjDefinition> pDefn);
+  uint32_t CurrentMaxObjDefinitionID() const;
+  CFXJS_ObjDefinition* ObjDefinitionForID(uint32_t id) const;
+  uint32_t AssignIDForObjDefinition(std::unique_ptr<CFXJS_ObjDefinition> pDefn);
+  V8TemplateMap* GetDynamicObjsMap() { return m_pDynamicObjsMap.get(); }
+  ExtensionIface* GetExtension() { return m_pExtension.get(); }
+  void SetExtension(std::unique_ptr<ExtensionIface> extension) {
+    m_pExtension = std::move(extension);
+  }
 
+ private:
+  explicit FXJS_PerIsolateData(v8::Isolate* pIsolate);
+
+  const wchar_t* const m_Tag;  // Raw, always a literal.
   std::vector<std::unique_ptr<CFXJS_ObjDefinition>> m_ObjectDefnArray;
   std::unique_ptr<V8TemplateMap> m_pDynamicObjsMap;
-  std::unique_ptr<ExtensionIface> m_pFXJSERuntimeData;
-
- protected:
-  explicit FXJS_PerIsolateData(v8::Isolate* pIsolate);
+  std::unique_ptr<ExtensionIface> m_pExtension;
 };
 
 void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate);
@@ -79,35 +85,39 @@
   explicit CFXJS_Engine(v8::Isolate* pIsolate);
   ~CFXJS_Engine() override;
 
-  using Constructor =
-      std::function<void(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj)>;
+  using Constructor = std::function<void(CFXJS_Engine* pEngine,
+                                         v8::Local<v8::Object> obj,
+                                         v8::Local<v8::Object> proxy)>;
   using Destructor = std::function<void(v8::Local<v8::Object> obj)>;
 
-  static int GetObjDefnID(v8::Local<v8::Object> pObj);
-  static CJS_Object* GetObjectPrivate(v8::Local<v8::Object> pObj);
+  static uint32_t GetObjDefnID(v8::Local<v8::Object> pObj);
+  static CJS_Object* GetObjectPrivate(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Object> pObj);
   static void SetObjectPrivate(v8::Local<v8::Object> pObj,
                                std::unique_ptr<CJS_Object> p);
   static void FreeObjectPrivate(v8::Local<v8::Object> pObj);
 
-  // Always returns a valid, newly-created objDefnID.
-  int DefineObj(const char* sObjName,
-                FXJSOBJTYPE eObjType,
-                Constructor pConstructor,
-                Destructor pDestructor);
+  // Always returns a valid (i.e. non-zero), newly-created objDefnID.
+  uint32_t DefineObj(const char* sObjName,
+                     FXJSOBJTYPE eObjType,
+                     Constructor pConstructor,
+                     Destructor pDestructor);
 
-  void DefineObjMethod(int nObjDefnID,
+  void DefineObjMethod(uint32_t nObjDefnID,
                        const char* sMethodName,
                        v8::FunctionCallback pMethodCall);
-  void DefineObjProperty(int nObjDefnID,
+  void DefineObjProperty(uint32_t nObjDefnID,
                          const char* sPropName,
                          v8::AccessorGetterCallback pPropGet,
                          v8::AccessorSetterCallback pPropPut);
-  void DefineObjAllProperties(int nObjDefnID,
-                              v8::GenericNamedPropertyQueryCallback pPropQurey,
-                              v8::GenericNamedPropertyGetterCallback pPropGet,
-                              v8::GenericNamedPropertySetterCallback pPropPut,
-                              v8::GenericNamedPropertyDeleterCallback pPropDel);
-  void DefineObjConst(int nObjDefnID,
+  void DefineObjAllProperties(
+      uint32_t nObjDefnID,
+      v8::GenericNamedPropertyQueryCallback pPropQurey,
+      v8::GenericNamedPropertyGetterCallback pPropGet,
+      v8::GenericNamedPropertySetterCallback pPropPut,
+      v8::GenericNamedPropertyDeleterCallback pPropDel,
+      v8::GenericNamedPropertyEnumeratorCallback pPropEnum);
+  void DefineObjConst(uint32_t nObjDefnID,
                       const char* sConstName,
                       v8::Local<v8::Value> pDefault);
   void DefineGlobalMethod(const char* sMethodName,
@@ -120,10 +130,11 @@
   void ReleaseEngine();
 
   // Called after FXJS_InitializeEngine call made.
-  Optional<IJS_Runtime::JS_Error> Execute(const WideString& script);
+  absl::optional<IJS_Runtime::JS_Error> Execute(const WideString& script);
 
   v8::Local<v8::Object> GetThisObj();
-  v8::Local<v8::Object> NewFXJSBoundObject(int nObjDefnID, FXJSOBJTYPE type);
+  v8::Local<v8::Object> NewFXJSBoundObject(uint32_t nObjDefnID,
+                                           FXJSOBJTYPE type);
   void Error(const WideString& message);
 
   v8::Local<v8::Context> GetV8Context();
diff --git a/fxjs/cfxjs_engine_embeddertest.cpp b/fxjs/cfxjs_engine_embeddertest.cpp
index c18171e..70e84ab 100644
--- a/fxjs/cfxjs_engine_embeddertest.cpp
+++ b/fxjs/cfxjs_engine_embeddertest.cpp
@@ -1,11 +1,16 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/cfxjs_engine.h"
 
+#include "testing/external_engine_embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/js_embedder_test.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-value.h"
 
 namespace {
 
@@ -19,7 +24,7 @@
 
 }  // namespace
 
-using CFXJSEngineEmbedderTest = JSEmbedderTest;
+class CFXJSEngineEmbedderTest : public ExternalEngineEmbedderTest {};
 
 void CheckAssignmentInEngineContext(CFXJS_Engine* current_engine,
                                     double expected) {
@@ -35,7 +40,8 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  Optional<IJS_Runtime::JS_Error> err = engine()->Execute(WideString(kScript1));
+  absl::optional<IJS_Runtime::JS_Error> err =
+      engine()->Execute(WideString(kScript1));
   EXPECT_FALSE(err);
   CheckAssignmentInEngineContext(engine(), kExpected1);
 }
@@ -52,7 +58,7 @@
 
   v8::Context::Scope context_scope(GetV8Context());
   {
-    Optional<IJS_Runtime::JS_Error> err =
+    absl::optional<IJS_Runtime::JS_Error> err =
         engine()->Execute(WideString(kScript0));
     EXPECT_FALSE(err);
     CheckAssignmentInEngineContext(engine(), kExpected0);
@@ -60,7 +66,8 @@
   {
     // engine1 executing in engine1's context doesn't affect main.
     v8::Context::Scope context_scope1(engine1.GetV8Context());
-    Optional<IJS_Runtime::JS_Error> err = engine1.Execute(WideString(kScript1));
+    absl::optional<IJS_Runtime::JS_Error> err =
+        engine1.Execute(WideString(kScript1));
     EXPECT_FALSE(err);
     CheckAssignmentInEngineContext(engine(), kExpected0);
     CheckAssignmentInEngineContext(&engine1, kExpected1);
@@ -68,7 +75,8 @@
   {
     // engine1 executing in engine2's context doesn't affect engine1.
     v8::Context::Scope context_scope2(engine2.GetV8Context());
-    Optional<IJS_Runtime::JS_Error> err = engine1.Execute(WideString(kScript2));
+    absl::optional<IJS_Runtime::JS_Error> err =
+        engine1.Execute(WideString(kScript2));
     EXPECT_FALSE(err);
     CheckAssignmentInEngineContext(engine(), kExpected0);
     CheckAssignmentInEngineContext(&engine1, kExpected1);
@@ -83,7 +91,7 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  Optional<IJS_Runtime::JS_Error> err =
+  absl::optional<IJS_Runtime::JS_Error> err =
       engine()->Execute(L"functoon(x) { return x+1; }");
   EXPECT_TRUE(err);
   EXPECT_STREQ(L"SyntaxError: Unexpected token '{'", err->exception.c_str());
@@ -96,11 +104,12 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  Optional<IJS_Runtime::JS_Error> err =
+  absl::optional<IJS_Runtime::JS_Error> err =
       engine()->Execute(L"let a = 3;\nundefined.colour");
   EXPECT_TRUE(err);
-  EXPECT_EQ(L"TypeError: Cannot read property 'colour' of undefined",
-            err->exception);
+  EXPECT_EQ(
+      L"TypeError: Cannot read properties of undefined (reading 'colour')",
+      err->exception);
   EXPECT_EQ(2, err->line);
   EXPECT_EQ(10, err->column);
 }
diff --git a/fxjs/cfxjs_engine_unittest.cpp b/fxjs/cfxjs_engine_unittest.cpp
index e0f7301..8d6c36b 100644
--- a/fxjs/cfxjs_engine_unittest.cpp
+++ b/fxjs/cfxjs_engine_unittest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,22 +6,23 @@
 
 #include <memory>
 
-#include "fxjs/cfx_v8_unittest.h"
 #include "fxjs/cjs_object.h"
+#include "testing/fxv8_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
 
 class FXJSEngineUnitTest : public FXV8UnitTest {
  public:
   FXJSEngineUnitTest() = default;
   ~FXJSEngineUnitTest() override = default;
 
+  // FXV8UnitTest:
   void SetUp() override {
     FXV8UnitTest::SetUp();
     FXJS_Initialize(1, isolate());
-    engine_ = pdfium::MakeUnique<CFXJS_Engine>(isolate());
+    engine_ = std::make_unique<CFXJS_Engine>(isolate());
   }
-
   void TearDown() override { FXJS_Release(); }
 
   CFXJS_Engine* engine() const { return engine_.get(); }
@@ -36,15 +37,22 @@
 static bool temp_destroyed = false;
 
 TEST_F(FXJSEngineUnitTest, GC) {
+  // Reset variables since there might be multiple iterations.
+  perm_created = false;
+  perm_destroyed = false;
+  temp_created = false;
+  temp_destroyed = false;
+
   v8::Isolate::Scope isolate_scope(isolate());
   v8::HandleScope handle_scope(isolate());
 
-  // Object: 0
+  // Object: 1
   engine()->DefineObj(
       "perm", FXJSOBJTYPE_DYNAMIC,
-      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj,
+         v8::Local<v8::Object> proxy) {
         pEngine->SetObjectPrivate(obj,
-                                  pdfium::MakeUnique<CJS_Object>(obj, nullptr));
+                                  std::make_unique<CJS_Object>(proxy, nullptr));
         perm_created = true;
       },
       [](v8::Local<v8::Object> obj) {
@@ -52,12 +60,13 @@
         CFXJS_Engine::SetObjectPrivate(obj, nullptr);
       });
 
-  // Object: 1
+  // Object: 2
   engine()->DefineObj(
       "temp", FXJSOBJTYPE_DYNAMIC,
-      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+      [](CFXJS_Engine* pEngine, v8::Local<v8::Object> obj,
+         v8::Local<v8::Object> proxy) {
         pEngine->SetObjectPrivate(obj,
-                                  pdfium::MakeUnique<CJS_Object>(obj, nullptr));
+                                  std::make_unique<CJS_Object>(proxy, nullptr));
         temp_created = true;
       },
       [](v8::Local<v8::Object> obj) {
@@ -69,7 +78,7 @@
 
   v8::Context::Scope context_scope(engine()->GetV8Context());
   v8::Local<v8::Object> perm =
-      engine()->NewFXJSBoundObject(0, FXJSOBJTYPE_DYNAMIC);
+      engine()->NewFXJSBoundObject(1, FXJSOBJTYPE_DYNAMIC);
   EXPECT_FALSE(perm.IsEmpty());
   EXPECT_TRUE(perm_created);
   EXPECT_FALSE(perm_destroyed);
@@ -77,13 +86,13 @@
   {
     v8::HandleScope inner_handle_scope(isolate());
     v8::Local<v8::Object> temp =
-        engine()->NewFXJSBoundObject(1, FXJSOBJTYPE_DYNAMIC);
+        engine()->NewFXJSBoundObject(2, FXJSOBJTYPE_DYNAMIC);
     EXPECT_FALSE(temp.IsEmpty());
     EXPECT_TRUE(temp_created);
     EXPECT_FALSE(temp_destroyed);
   }
 
-  Optional<IJS_Runtime::JS_Error> err = engine()->Execute(L"gc();");
+  absl::optional<IJS_Runtime::JS_Error> err = engine()->Execute(L"gc();");
   EXPECT_FALSE(err);
 
   EXPECT_TRUE(perm_created);
diff --git a/fxjs/cjs_annot.cpp b/fxjs/cjs_annot.cpp
index 292e586..cee6111 100644
--- a/fxjs/cjs_annot.cpp
+++ b/fxjs/cjs_annot.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include "fxjs/cjs_annot.h"
 
 #include "constants/annotation_flags.h"
-#include "fpdfsdk/cpdfsdk_baannot.h"
 #include "fxjs/cjs_event_context.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
@@ -18,12 +17,12 @@
     {"name", get_name_static, set_name_static},
     {"type", get_type_static, set_type_static}};
 
-int CJS_Annot::ObjDefnID = -1;
+uint32_t CJS_Annot::ObjDefnID = 0;
 
 const char CJS_Annot::kName[] = "Annot";
 
 // static
-int CJS_Annot::GetObjDefnID() {
+uint32_t CJS_Annot::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -47,7 +46,7 @@
   if (!m_pAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Annot* pPDFAnnot = m_pAnnot->AsBAAnnot()->GetPDFAnnot();
+  CPDF_Annot* pPDFAnnot = m_pAnnot->GetPDFAnnot();
   return CJS_Result::Success(pRuntime->NewBoolean(pPDFAnnot->IsHidden()));
 }
 
@@ -56,7 +55,7 @@
   // May invalidate m_pAnnot.
   bool bHidden = pRuntime->ToBoolean(vp);
 
-  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get();
   if (!pBAAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -77,7 +76,7 @@
 }
 
 CJS_Result CJS_Annot::get_name(CJS_Runtime* pRuntime) {
-  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get();
   if (!pBAAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -89,7 +88,7 @@
   // May invalidate m_pAnnot.
   WideString annotName = pRuntime->ToWideString(vp);
 
-  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get();
   if (!pBAAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -98,14 +97,12 @@
 }
 
 CJS_Result CJS_Annot::get_type(CJS_Runtime* pRuntime) {
-  CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get());
+  CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get();
   if (!pBAAnnot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   return CJS_Result::Success(pRuntime->NewString(
-      WideString::FromDefANSI(
-          CPDF_Annot::AnnotSubtypeToString(pBAAnnot->GetAnnotSubtype())
-              .AsStringView())
+      CPDF_Annot::AnnotSubtypeToString(pBAAnnot->GetAnnotSubtype())
           .AsStringView()));
 }
 
diff --git a/fxjs/cjs_annot.h b/fxjs/cjs_annot.h
index ceb2615..0e53407 100644
--- a/fxjs/cjs_annot.h
+++ b/fxjs/cjs_annot.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,13 @@
 #ifndef FXJS_CJS_ANNOT_H_
 #define FXJS_CJS_ANNOT_H_
 
+#include "fpdfsdk/cpdfsdk_baannot.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
 
-class CPDFSDK_BAAnnot;
-
 class CJS_Annot final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Annot(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -27,7 +26,7 @@
   JS_STATIC_PROP(type, type, CJS_Annot)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
 
@@ -40,7 +39,7 @@
   CJS_Result get_type(CJS_Runtime* pRuntime);
   CJS_Result set_type(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
 
-  ObservedPtr<CPDFSDK_Annot> m_pAnnot;
+  ObservedPtr<CPDFSDK_BAAnnot> m_pAnnot;
 };
 
 #endif  // FXJS_CJS_ANNOT_H_
diff --git a/fxjs/cjs_app.cpp b/fxjs/cjs_app.cpp
index 992fd98..0784e7e 100644
--- a/fxjs/cjs_app.cpp
+++ b/fxjs/cjs_app.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,24 +6,33 @@
 
 #include "fxjs/cjs_app.h"
 
+#include <stdint.h>
+
+#include <algorithm>
 #include <utility>
 
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+#include "core/fxcrt/stl_util.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_timerobj.h"
 #include "fxjs/global_timer.h"
 #include "fxjs/ijs_event_context.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "v8/include/v8-container.h"
 
-#define JS_STR_VIEWERTYPE L"pdfium"
-#define JS_STR_VIEWERVARIATION L"Full"
-#define JS_STR_PLATFORM L"WIN"
-#define JS_STR_LANGUAGE L"ENU"
-#define JS_NUM_VIEWERVERSION 8
-#define JS_NUM_VIEWERVERSION_XFA 11
-#define JS_NUM_FORMSVERSION 7
+namespace {
+
+constexpr wchar_t kStrViewerType[] = L"pdfium";
+constexpr wchar_t kStrViewerVariation[] = L"Full";
+constexpr wchar_t kStrPlatform[] = L"WIN";
+constexpr wchar_t kStrLanguage[] = L"ENU";
+constexpr int kNumViewerVersion = 8;
+constexpr int kNumViewerVersionXfa = 11;
+constexpr int kNumFormsVersion = 7;
+
+}  // namespace
 
 const JSPropertySpec CJS_App::PropertySpecs[] = {
     {"activeDocs", get_active_docs_static, set_active_docs_static},
@@ -64,12 +73,12 @@
     {"setInterval", setInterval_static},
     {"setTimeOut", setTimeOut_static}};
 
-int CJS_App::ObjDefnID = -1;
+uint32_t CJS_App::ObjDefnID = 0;
 
 const char CJS_App::kName[] = "app";
 
 // static
-int CJS_App::GetObjDefnID() {
+uint32_t CJS_App::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -88,7 +97,7 @@
 
 CJS_Result CJS_App::get_active_docs(CJS_Runtime* pRuntime) {
   v8::Local<v8::Object> pObj = pRuntime->GetThisObj();
-  auto pJSDocument = JSGetObject<CJS_Document>(pObj);
+  auto pJSDocument = JSGetObject<CJS_Document>(pRuntime->GetIsolate(), pObj);
   if (!pJSDocument)
     return CJS_Result::Failure(JSMessage::kObjectTypeError);
   v8::Local<v8::Array> aDocs = pRuntime->NewArray();
@@ -117,7 +126,7 @@
 }
 
 CJS_Result CJS_App::get_forms_version(CJS_Runtime* pRuntime) {
-  return CJS_Result::Success(pRuntime->NewNumber(JS_NUM_FORMSVERSION));
+  return CJS_Result::Success(pRuntime->NewNumber(kNumFormsVersion));
 }
 
 CJS_Result CJS_App::set_forms_version(CJS_Runtime* pRuntime,
@@ -126,7 +135,7 @@
 }
 
 CJS_Result CJS_App::get_viewer_type(CJS_Runtime* pRuntime) {
-  return CJS_Result::Success(pRuntime->NewString(JS_STR_VIEWERTYPE));
+  return CJS_Result::Success(pRuntime->NewString(kStrViewerType));
 }
 
 CJS_Result CJS_App::set_viewer_type(CJS_Runtime* pRuntime,
@@ -135,7 +144,7 @@
 }
 
 CJS_Result CJS_App::get_viewer_variation(CJS_Runtime* pRuntime) {
-  return CJS_Result::Success(pRuntime->NewString(JS_STR_VIEWERVARIATION));
+  return CJS_Result::Success(pRuntime->NewString(kStrViewerVariation));
 }
 
 CJS_Result CJS_App::set_viewer_variation(CJS_Runtime* pRuntime,
@@ -147,8 +156,8 @@
   CPDF_Document::Extension* pContext =
       pRuntime->GetFormFillEnv()->GetDocExtension();
   int version = pContext && pContext->ContainsExtensionForm()
-                    ? JS_NUM_VIEWERVERSION_XFA
-                    : JS_NUM_VIEWERVERSION;
+                    ? kNumViewerVersionXfa
+                    : kNumViewerVersion;
   return CJS_Result::Success(pRuntime->NewNumber(version));
 }
 
@@ -164,7 +173,7 @@
     if (!platform.IsEmpty())
       return CJS_Result::Success(pRuntime->NewString(platform.AsStringView()));
   }
-  return CJS_Result::Success(pRuntime->NewString(JS_STR_PLATFORM));
+  return CJS_Result::Success(pRuntime->NewString(kStrPlatform));
 }
 
 CJS_Result CJS_App::set_platform(CJS_Runtime* pRuntime,
@@ -179,7 +188,7 @@
     if (!language.IsEmpty())
       return CJS_Result::Success(pRuntime->NewString(language.AsStringView()));
   }
-  return CJS_Result::Success(pRuntime->NewString(JS_STR_LANGUAGE));
+  return CJS_Result::Success(pRuntime->NewString(kStrLanguage));
 }
 
 CJS_Result CJS_App::set_language(CJS_Runtime* pRuntime,
@@ -250,7 +259,7 @@
     swTitle = JSGetStringFromID(JSMessage::kAlert);
 
   pRuntime->BeginBlock();
-  pFormFillEnv->KillFocusAnnot(0);
+  pFormFillEnv->KillFocusAnnot({});
   v8::Local<v8::Value> ret = pRuntime->NewNumber(
       pFormFillEnv->JS_appAlert(swMsg, swTitle, iType, iIcon));
   pRuntime->EndBlock();
@@ -302,7 +311,7 @@
     return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
   uint32_t dwInterval = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000;
-  auto timerRef = pdfium::MakeUnique<GlobalTimer>(
+  auto timerRef = std::make_unique<GlobalTimer>(
       this, pRuntime, GlobalTimer::Type::kRepeating, script, dwInterval, 0);
   GlobalTimer* pTimerRef = timerRef.get();
   m_Timers.insert(std::move(timerRef));
@@ -312,8 +321,8 @@
   if (pRetObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJS_TimerObj =
-      static_cast<CJS_TimerObj*>(CFXJS_Engine::GetObjectPrivate(pRetObj));
+  auto* pJS_TimerObj = static_cast<CJS_TimerObj*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pRetObj));
 
   pJS_TimerObj->SetTimer(pTimerRef);
   return CJS_Result::Success(pRetObj);
@@ -330,9 +339,9 @@
     return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
   uint32_t dwTimeOut = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000;
-  auto timerRef = pdfium::MakeUnique<GlobalTimer>(this, pRuntime,
-                                                  GlobalTimer::Type::kOneShot,
-                                                  script, dwTimeOut, dwTimeOut);
+  auto timerRef =
+      std::make_unique<GlobalTimer>(this, pRuntime, GlobalTimer::Type::kOneShot,
+                                    script, dwTimeOut, dwTimeOut);
   GlobalTimer* pTimerRef = timerRef.get();
   m_Timers.insert(std::move(timerRef));
 
@@ -341,8 +350,8 @@
   if (pRetObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJS_TimerObj =
-      static_cast<CJS_TimerObj*>(CFXJS_Engine::GetObjectPrivate(pRetObj));
+  auto* pJS_TimerObj = static_cast<CJS_TimerObj*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pRetObj));
 
   pJS_TimerObj->SetTimer(pTimerRef);
   return CJS_Result::Success(pRetObj);
@@ -374,7 +383,7 @@
     return;
 
   v8::Local<v8::Object> pObj = pRuntime->ToObject(param);
-  auto pTimer = JSGetObject<CJS_TimerObj>(pObj);
+  auto pTimer = JSGetObject<CJS_TimerObj>(pRuntime->GetIsolate(), pObj);
   if (!pTimer)
     return;
 
@@ -394,7 +403,7 @@
 }
 
 void CJS_App::CancelProc(GlobalTimer* pTimer) {
-  m_Timers.erase(pdfium::FakeUniquePtr<GlobalTimer>(pTimer));
+  m_Timers.erase(fxcrt::MakeFakeUniquePtr(pTimer));
 }
 
 void CJS_App::RunJsScript(CJS_Runtime* pRuntime, const WideString& wsScript) {
@@ -453,8 +462,8 @@
     cMsg = pRuntime->ToWideString(newParams[5]);
 
   pRuntime->BeginBlock();
-  pRuntime->GetFormFillEnv()->JS_docmailForm(nullptr, 0, bUI, cTo, cSubject,
-                                             cCc, cBcc, cMsg);
+  pRuntime->GetFormFillEnv()->JS_docmailForm(pdfium::span<const uint8_t>(), bUI,
+                                             cTo, cSubject, cCc, cBcc, cMsg);
   pRuntime->EndBlock();
   return CJS_Result::Success();
 }
@@ -541,18 +550,20 @@
   if (IsExpandedParamKnown(newParams[4]))
     swLabel = pRuntime->ToWideString(newParams[4]);
 
-  const int MAX_INPUT_BYTES = 2048;
-  std::vector<uint8_t> pBuff(MAX_INPUT_BYTES + 2);
-  int nLengthBytes = pRuntime->GetFormFillEnv()->JS_appResponse(
-      swQuestion, swTitle, swDefault, swLabel, bPassword, pBuff.data(),
-      MAX_INPUT_BYTES);
-
-  if (nLengthBytes < 0 || nLengthBytes > MAX_INPUT_BYTES)
+  constexpr int kMaxWideChars = 1024;
+  constexpr int kMaxBytes = kMaxWideChars * sizeof(uint16_t);
+  FixedZeroedDataVector<uint16_t> buffer(kMaxWideChars);
+  pdfium::span<uint16_t> buffer_span = buffer.writable_span();
+  int byte_length = pRuntime->GetFormFillEnv()->JS_appResponse(
+      swQuestion, swTitle, swDefault, swLabel, bPassword,
+      pdfium::as_writable_bytes(buffer_span));
+  if (byte_length < 0 || byte_length > kMaxBytes)
     return CJS_Result::Failure(JSMessage::kParamTooLongError);
 
+  buffer_span = buffer_span.first(
+      std::min<size_t>(kMaxWideChars, byte_length / sizeof(uint16_t)));
   return CJS_Result::Success(pRuntime->NewString(
-      WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
-                              nLengthBytes / sizeof(uint16_t))
+      WideString::FromUTF16LE(buffer_span.data(), buffer_span.size())
           .AsStringView()));
 }
 
diff --git a/fxjs/cjs_app.h b/fxjs/cjs_app.h
index 524a7f0..23b5c1f 100644
--- a/fxjs/cjs_app.h
+++ b/fxjs/cjs_app.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,7 +19,7 @@
 
 class CJS_App final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_App(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -66,7 +66,7 @@
   JS_STATIC_METHOD(setTimeOut, CJS_App)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
diff --git a/fxjs/cjs_border.cpp b/fxjs/cjs_border.cpp
index 35204d7..b8edd09 100644
--- a/fxjs/cjs_border.cpp
+++ b/fxjs/cjs_border.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
     {"i", JSConstSpec::String, 0, "inset"},
     {"u", JSConstSpec::String, 0, "underline"}};
 
-int CJS_Border::ObjDefnID = -1;
+uint32_t CJS_Border::ObjDefnID = 0;
 
 // static
 void CJS_Border::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_border.h b/fxjs/cjs_border.h
index 0a306f7..29a9251 100644
--- a/fxjs/cjs_border.h
+++ b/fxjs/cjs_border.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Border() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_color.cpp b/fxjs/cjs_color.cpp
index 2b75e75..22b31fc 100644
--- a/fxjs/cjs_color.cpp
+++ b/fxjs/cjs_color.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,10 +11,11 @@
 
 #include "core/fxge/cfx_color.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_runtime.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_define.h"
+#include "v8/include/v8-container.h"
 
 const JSPropertySpec CJS_Color::PropertySpecs[] = {
     {"black", get_black_static, set_black_static},
@@ -33,11 +34,11 @@
 const JSMethodSpec CJS_Color::MethodSpecs[] = {{"convert", convert_static},
                                                {"equal", equal_static}};
 
-int CJS_Color::ObjDefnID = -1;
+uint32_t CJS_Color::ObjDefnID = 0;
 const char CJS_Color::kName[] = "color";
 
 // static
-int CJS_Color::GetObjDefnID() {
+uint32_t CJS_Color::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -54,23 +55,23 @@
                                                        const CFX_Color& color) {
   v8::Local<v8::Array> array;
   switch (color.nColorType) {
-    case CFX_Color::kTransparent:
+    case CFX_Color::Type::kTransparent:
       array = pRuntime->NewArray();
       pRuntime->PutArrayElement(array, 0, pRuntime->NewString("T"));
       break;
-    case CFX_Color::kGray:
+    case CFX_Color::Type::kGray:
       array = pRuntime->NewArray();
       pRuntime->PutArrayElement(array, 0, pRuntime->NewString("G"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
       break;
-    case CFX_Color::kRGB:
+    case CFX_Color::Type::kRGB:
       array = pRuntime->NewArray();
       pRuntime->PutArrayElement(array, 0, pRuntime->NewString("RGB"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
       pRuntime->PutArrayElement(array, 2, pRuntime->NewNumber(color.fColor2));
       pRuntime->PutArrayElement(array, 3, pRuntime->NewNumber(color.fColor3));
       break;
-    case CFX_Color::kCMYK:
+    case CFX_Color::Type::kCMYK:
       array = pRuntime->NewArray();
       pRuntime->PutArrayElement(array, 0, pRuntime->NewString("CMYK"));
       pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1));
@@ -85,14 +86,14 @@
 // static
 CFX_Color CJS_Color::ConvertArrayToPWLColor(CJS_Runtime* pRuntime,
                                             v8::Local<v8::Array> array) {
-  int nArrayLen = pRuntime->GetArrayLength(array);
-  if (nArrayLen < 1)
+  size_t nArrayLen = pRuntime->GetArrayLength(array);
+  if (nArrayLen == 0)
     return CFX_Color();
 
   WideString sSpace =
       pRuntime->ToWideString(pRuntime->GetArrayElement(array, 0));
   if (sSpace.EqualsASCII("T"))
-    return CFX_Color(CFX_Color::kTransparent);
+    return CFX_Color(CFX_Color::Type::kTransparent);
 
   float d1 = 0;
   if (nArrayLen > 1) {
@@ -100,7 +101,7 @@
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 1)));
   }
   if (sSpace.EqualsASCII("G"))
-    return CFX_Color(CFX_Color::kGray, d1);
+    return CFX_Color(CFX_Color::Type::kGray, d1);
 
   float d2 = 0;
   float d3 = 0;
@@ -113,7 +114,7 @@
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 3)));
   }
   if (sSpace.EqualsASCII("RGB"))
-    return CFX_Color(CFX_Color::kRGB, d1, d2, d3);
+    return CFX_Color(CFX_Color::Type::kRGB, d1, d2, d3);
 
   float d4 = 0;
   if (nArrayLen > 4) {
@@ -121,25 +122,25 @@
         pRuntime->ToDouble(pRuntime->GetArrayElement(array, 4)));
   }
   if (sSpace.EqualsASCII("CMYK"))
-    return CFX_Color(CFX_Color::kCMYK, d1, d2, d3, d4);
+    return CFX_Color(CFX_Color::Type::kCMYK, d1, d2, d3, d4);
 
   return CFX_Color();
 }
 
 CJS_Color::CJS_Color(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
     : CJS_Object(pObject, pRuntime),
-      m_crTransparent(CFX_Color::kTransparent),
-      m_crBlack(CFX_Color::kGray, 0),
-      m_crWhite(CFX_Color::kGray, 1),
-      m_crRed(CFX_Color::kRGB, 1, 0, 0),
-      m_crGreen(CFX_Color::kRGB, 0, 1, 0),
-      m_crBlue(CFX_Color::kRGB, 0, 0, 1),
-      m_crCyan(CFX_Color::kCMYK, 1, 0, 0, 0),
-      m_crMagenta(CFX_Color::kCMYK, 0, 1, 0, 0),
-      m_crYellow(CFX_Color::kCMYK, 0, 0, 1, 0),
-      m_crDKGray(CFX_Color::kGray, 0.25),
-      m_crGray(CFX_Color::kGray, 0.5),
-      m_crLTGray(CFX_Color::kGray, 0.75) {}
+      m_crTransparent(CFX_Color::Type::kTransparent),
+      m_crBlack(CFX_Color::Type::kGray, 0),
+      m_crWhite(CFX_Color::Type::kGray, 1),
+      m_crRed(CFX_Color::Type::kRGB, 1, 0, 0),
+      m_crGreen(CFX_Color::Type::kRGB, 0, 1, 0),
+      m_crBlue(CFX_Color::Type::kRGB, 0, 0, 1),
+      m_crCyan(CFX_Color::Type::kCMYK, 1, 0, 0, 0),
+      m_crMagenta(CFX_Color::Type::kCMYK, 0, 1, 0, 0),
+      m_crYellow(CFX_Color::Type::kCMYK, 0, 0, 1, 0),
+      m_crDKGray(CFX_Color::Type::kGray, 0.25),
+      m_crGray(CFX_Color::Type::kGray, 0.5),
+      m_crLTGray(CFX_Color::Type::kGray, 0.75) {}
 
 CJS_Color::~CJS_Color() = default;
 
@@ -273,19 +274,19 @@
   if (params.size() < 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  if (params[0].IsEmpty() || !params[0]->IsArray())
+  if (!fxv8::IsArray(params[0]))
     return CJS_Result::Failure(JSMessage::kTypeError);
 
   WideString sDestSpace = pRuntime->ToWideString(params[1]);
-  int nColorType = CFX_Color::kTransparent;
+  CFX_Color::Type nColorType = CFX_Color::Type::kTransparent;
   if (sDestSpace.EqualsASCII("T"))
-    nColorType = CFX_Color::kTransparent;
+    nColorType = CFX_Color::Type::kTransparent;
   else if (sDestSpace.EqualsASCII("G"))
-    nColorType = CFX_Color::kGray;
+    nColorType = CFX_Color::Type::kGray;
   else if (sDestSpace.EqualsASCII("RGB"))
-    nColorType = CFX_Color::kRGB;
+    nColorType = CFX_Color::Type::kRGB;
   else if (sDestSpace.EqualsASCII("CMYK"))
-    nColorType = CFX_Color::kCMYK;
+    nColorType = CFX_Color::Type::kCMYK;
 
   CFX_Color color =
       ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[0]));
@@ -302,10 +303,8 @@
   if (params.size() < 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  if (params[0].IsEmpty() || !params[0]->IsArray() || params[1].IsEmpty() ||
-      !params[1]->IsArray()) {
+  if (!fxv8::IsArray(params[0]) || !fxv8::IsArray(params[1]))
     return CJS_Result::Failure(JSMessage::kTypeError);
-  }
 
   CFX_Color color1 =
       ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[0]));
@@ -313,7 +312,7 @@
       ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[1]));
 
   // Relies on higher values having more components.
-  int32_t best = std::max(color1.nColorType, color2.nColorType);
+  CFX_Color::Type best = std::max(color1.nColorType, color2.nColorType);
   return CJS_Result::Success(pRuntime->NewBoolean(
       color1.ConvertColorType(best) == color2.ConvertColorType(best)));
 }
diff --git a/fxjs/cjs_color.h b/fxjs/cjs_color.h
index 03aa735..b768709 100644
--- a/fxjs/cjs_color.h
+++ b/fxjs/cjs_color.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,12 +9,13 @@
 
 #include <vector>
 
+#include "core/fxge/cfx_color.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
 
 class CJS_Color final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
   static v8::Local<v8::Array> ConvertPWLColorToArray(CJS_Runtime* pRuntime,
                                                      const CFX_Color& color);
@@ -41,7 +42,7 @@
   JS_STATIC_METHOD(equal, CJS_Color)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
diff --git a/fxjs/cjs_console.cpp b/fxjs/cjs_console.cpp
index c696bbd..91c2ca0 100644
--- a/fxjs/cjs_console.cpp
+++ b/fxjs/cjs_console.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
 
@@ -18,11 +17,11 @@
                                                  {"println", println_static},
                                                  {"show", show_static}};
 
-int CJS_Console::ObjDefnID = -1;
+uint32_t CJS_Console::ObjDefnID = 0;
 const char CJS_Console::kName[] = "console";
 
 // static
-int CJS_Console::GetObjDefnID() {
+uint32_t CJS_Console::GetObjDefnID() {
   return ObjDefnID;
 }
 
diff --git a/fxjs/cjs_console.h b/fxjs/cjs_console.h
index 2bc118a..5608d55 100644
--- a/fxjs/cjs_console.h
+++ b/fxjs/cjs_console.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJS_Console final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Console(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -26,7 +26,7 @@
   JS_STATIC_METHOD(show, CJS_Console)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSMethodSpec MethodSpecs[];
 
diff --git a/fxjs/cjs_delaydata.cpp b/fxjs/cjs_delaydata.cpp
index d7e1f78..12b34fa 100644
--- a/fxjs/cjs_delaydata.cpp
+++ b/fxjs/cjs_delaydata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,4 +9,4 @@
 CJS_DelayData::CJS_DelayData(FIELD_PROP prop, int idx, const WideString& name)
     : eProp(prop), nControlIndex(idx), sFieldName(name) {}
 
-CJS_DelayData::~CJS_DelayData() {}
+CJS_DelayData::~CJS_DelayData() = default;
diff --git a/fxjs/cjs_delaydata.h b/fxjs/cjs_delaydata.h
index f4c7820..861dbab 100644
--- a/fxjs/cjs_delaydata.h
+++ b/fxjs/cjs_delaydata.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,9 +20,9 @@
 
   FIELD_PROP eProp;
   int nControlIndex;
+  int32_t num = 0;
+  bool b = false;
   WideString sFieldName;
-  int32_t num;
-  bool b;
   ByteString bytestring;
   WideString widestring;
   CFX_FloatRect rect;
diff --git a/fxjs/cjs_display.cpp b/fxjs/cjs_display.cpp
index 71d6c02..0e0ee26 100644
--- a/fxjs/cjs_display.cpp
+++ b/fxjs/cjs_display.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
     {"noPrint", JSConstSpec::Number, 2, 0},
     {"noView", JSConstSpec::Number, 3, 0}};
 
-int CJS_Display::ObjDefnID = -1;
+uint32_t CJS_Display::ObjDefnID = 0;
 
 // static
 void CJS_Display::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_display.h b/fxjs/cjs_display.h
index 59b6e9c..7de5368 100644
--- a/fxjs/cjs_display.h
+++ b/fxjs/cjs_display.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Display() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_document.cpp b/fxjs/cjs_document.cpp
index 9294f17..5a842ce 100644
--- a/fxjs/cjs_document.cpp
+++ b/fxjs/cjs_document.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,22 @@
 
 #include "fxjs/cjs_document.h"
 
+#include <stdint.h>
+
 #include <utility>
 
+#include "constants/access_permissions.h"
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/page/cpdf_textobject.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
 #include "fpdfsdk/cpdfsdk_annotiteration.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fxjs/cjs_annot.h"
@@ -27,6 +31,8 @@
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_icon.h"
 #include "fxjs/js_resources.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-container.h"
 
 const JSPropertySpec CJS_Document::PropertySpecs[] = {
     {"ADBE", get_ADBE_static, set_ADBE_static},
@@ -108,11 +114,11 @@
     {"submitForm", submitForm_static},
     {"syncAnnotScan", syncAnnotScan_static}};
 
-int CJS_Document::ObjDefnID = -1;
+uint32_t CJS_Document::ObjDefnID = 0;
 const char CJS_Document::kName[] = "Document";
 
 // static
-int CJS_Document::GetObjDefnID() {
+uint32_t CJS_Document::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -255,8 +261,8 @@
   if (pFieldObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJSField =
-      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
+  auto* pJSField = static_cast<CJS_Field*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pFieldObj));
   if (!pJSField)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -340,8 +346,8 @@
     cMsg = pRuntime->ToWideString(newParams[5]);
 
   pRuntime->BeginBlock();
-  m_pFormFillEnv->JS_docmailForm(nullptr, 0, bUI, cTo, cSubject, cCc, cBcc,
-                                 cMsg);
+  m_pFormFillEnv->JS_docmailForm(pdfium::span<const uint8_t>(), bUI, cTo,
+                                 cSubject, cCc, cBcc, cMsg);
   pRuntime->EndBlock();
   return CJS_Result::Success();
 }
@@ -354,7 +360,9 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
+
+  using pdfium::access_permissions::kExtractForAccessibility;
+  if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility))
     return CJS_Result::Failure(JSMessage::kPermissionError);
 
   CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm();
@@ -389,10 +397,9 @@
   if (IsExpandedParamKnown(newParams[5]))
     cMsg = pRuntime->ToWideString(newParams[5]);
 
-  std::vector<char> mutable_buf(sTextBuf.begin(), sTextBuf.end());
   pRuntime->BeginBlock();
-  m_pFormFillEnv->JS_docmailForm(mutable_buf.data(), mutable_buf.size(), bUI,
-                                 cTo, cSubject, cCc, cBcc, cMsg);
+  m_pFormFillEnv->JS_docmailForm(sTextBuf.raw_span(), bUI, cTo, cSubject, cCc,
+                                 cBcc, cMsg);
   pRuntime->EndBlock();
   return CJS_Result::Success();
 }
@@ -439,8 +446,7 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_EventRecorder* pHandler =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pHandler = pRuntime->GetCurrentEventContext();
   if (!pHandler->IsUserGesture())
     return CJS_Result::Failure(JSMessage::kUserGestureRequiredError);
 
@@ -460,30 +466,30 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM))) {
+  if (!m_pFormFillEnv->HasPermissions(
+          pdfium::access_permissions::kModifyContent |
+          pdfium::access_permissions::kModifyAnnotation)) {
     return CJS_Result::Failure(JSMessage::kPermissionError);
   }
 
   WideString sFieldName = pRuntime->ToWideString(params[0]);
   CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm();
-  std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+  std::vector<ObservedPtr<CPDFSDK_Widget>> widgets;
   pInteractiveForm->GetWidgets(sFieldName, &widgets);
   if (widgets.empty())
     return CJS_Result::Success();
 
-  for (const auto& pAnnot : widgets) {
-    CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot.Get());
+  for (const auto& pWidget : widgets) {
     if (!pWidget)
       continue;
 
     IPDF_Page* pPage = pWidget->GetPage();
-    ASSERT(pPage);
+    DCHECK(pPage);
 
     // If there is currently no pageview associated with the page being used
     // do not create one. We may be in the process of tearing down the document
     // and creating a new pageview at this point will cause bad things.
-    CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage, false);
+    CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage);
     if (!pPageView)
       continue;
 
@@ -506,15 +512,17 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM))) {
+
+  if (!m_pFormFillEnv->HasPermissions(
+          pdfium::access_permissions::kModifyContent |
+          pdfium::access_permissions::kModifyAnnotation |
+          pdfium::access_permissions::kFillForm)) {
     return CJS_Result::Failure(JSMessage::kPermissionError);
   }
 
   CPDF_InteractiveForm* pPDFForm = GetCoreInteractiveForm();
   if (params.empty()) {
-    pPDFForm->ResetForm(NotificationOption::kNotify);
+    pPDFForm->ResetForm();
     m_pFormFillEnv->SetChangeMark();
     return CJS_Result::Success();
   }
@@ -531,12 +539,13 @@
   for (size_t i = 0; i < pRuntime->GetArrayLength(array); ++i) {
     WideString swVal =
         pRuntime->ToWideString(pRuntime->GetArrayElement(array, i));
-    for (int j = 0, jsz = pPDFForm->CountFields(swVal); j < jsz; ++j)
+    const size_t jsz = pPDFForm->CountFields(swVal);
+    for (size_t j = 0; j < jsz; ++j)
       aFields.push_back(pPDFForm->GetField(j, swVal));
   }
 
   if (!aFields.empty()) {
-    pPDFForm->ResetForm(aFields, true, NotificationOption::kNotify);
+    pPDFForm->ResetForm(aFields, true);
     m_pFormFillEnv->SetChangeMark();
   }
 
@@ -565,8 +574,7 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CJS_EventRecorder* pHandler =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pHandler = pRuntime->GetCurrentEventContext();
   if (!pHandler->IsUserGesture())
     return CJS_Result::Failure(JSMessage::kUserGestureRequiredError);
 
@@ -597,7 +605,7 @@
   if (pRuntime->GetArrayLength(aFields) == 0 && bEmpty) {
     if (pPDFForm->CheckRequiredFields(nullptr, true)) {
       pRuntime->BeginBlock();
-      GetSDKInteractiveForm()->SubmitForm(strURL, false);
+      GetSDKInteractiveForm()->SubmitForm(strURL);
       pRuntime->EndBlock();
     }
     return CJS_Result::Success();
@@ -607,7 +615,8 @@
   for (size_t i = 0; i < pRuntime->GetArrayLength(aFields); ++i) {
     WideString sName =
         pRuntime->ToWideString(pRuntime->GetArrayElement(aFields, i));
-    for (int j = 0, jsz = pPDFForm->CountFields(sName); j < jsz; ++j) {
+    const size_t jsz = pPDFForm->CountFields(sName);
+    for (size_t j = 0; j < jsz; ++j) {
       CPDF_FormField* pField = pPDFForm->GetField(j, sName);
       if (!bEmpty && pField->GetValue().IsEmpty())
         continue;
@@ -643,14 +652,16 @@
 
 CJS_Result CJS_Document::set_author(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Author");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_info(CJS_Runtime* pRuntime) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  const auto* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
+  RetainPtr<const CPDF_Dictionary> pDictionary =
+      m_pFormFillEnv->GetPDFDocument()->GetInfo();
   if (!pDictionary)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -685,11 +696,10 @@
                               pRuntime->NewString(cwTrapped.AsStringView()));
 
   // PutObjectProperty() calls below may re-enter JS and change info dict.
-  auto pCopy = pDictionary->Clone();
-  CPDF_DictionaryLocker locker(ToDictionary(pCopy.Get()));
+  CPDF_DictionaryLocker locker(ToDictionary(pDictionary->Clone()));
   for (const auto& it : locker) {
     const ByteString& bsKey = it.first;
-    CPDF_Object* pValueObj = it.second.Get();
+    const RetainPtr<CPDF_Object>& pValueObj = it.second;
     if (pValueObj->IsString() || pValueObj->IsName()) {
       pRuntime->PutObjectProperty(
           pObj, bsKey.AsStringView(),
@@ -716,38 +726,23 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Dictionary* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
+  RetainPtr<CPDF_Dictionary> pDictionary =
+      m_pFormFillEnv->GetPDFDocument()->GetInfo();
   if (!pDictionary)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
+
   return CJS_Result::Success(pRuntime->NewString(
       pDictionary->GetUnicodeTextFor(propName).AsStringView()));
 }
 
-CJS_Result CJS_Document::setPropertyInternal(CJS_Runtime* pRuntime,
-                                             v8::Local<v8::Value> vp,
-                                             const ByteString& propName) {
-  if (!m_pFormFillEnv)
-    return CJS_Result::Failure(JSMessage::kBadObjectError);
-
-  CPDF_Dictionary* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo();
-  if (!pDictionary)
-    return CJS_Result::Failure(JSMessage::kBadObjectError);
-
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY))
-    return CJS_Result::Failure(JSMessage::kPermissionError);
-
-  pDictionary->SetNewFor<CPDF_String>(propName, pRuntime->ToWideString(vp));
-  m_pFormFillEnv->SetChangeMark();
-  return CJS_Result::Success();
-}
-
 CJS_Result CJS_Document::get_creation_date(CJS_Runtime* pRuntime) {
   return getPropertyInternal(pRuntime, "CreationDate");
 }
 
 CJS_Result CJS_Document::set_creation_date(CJS_Runtime* pRuntime,
                                            v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "CreationDate");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_creator(CJS_Runtime* pRuntime) {
@@ -756,7 +751,8 @@
 
 CJS_Result CJS_Document::set_creator(CJS_Runtime* pRuntime,
                                      v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Creator");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_delay(CJS_Runtime* pRuntime) {
@@ -769,7 +765,9 @@
                                    v8::Local<v8::Value> vp) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY))
+
+  using pdfium::access_permissions::kModifyContent;
+  if (!m_pFormFillEnv->HasPermissions(kModifyContent))
     return CJS_Result::Failure(JSMessage::kPermissionError);
 
   m_bDelay = pRuntime->ToBoolean(vp);
@@ -792,7 +790,8 @@
 
 CJS_Result CJS_Document::set_keywords(CJS_Runtime* pRuntime,
                                       v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Keywords");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_mod_date(CJS_Runtime* pRuntime) {
@@ -801,7 +800,8 @@
 
 CJS_Result CJS_Document::set_mod_date(CJS_Runtime* pRuntime,
                                       v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "ModDate");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_producer(CJS_Runtime* pRuntime) {
@@ -810,7 +810,8 @@
 
 CJS_Result CJS_Document::set_producer(CJS_Runtime* pRuntime,
                                       v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Producer");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_subject(CJS_Runtime* pRuntime) {
@@ -819,7 +820,8 @@
 
 CJS_Result CJS_Document::set_subject(CJS_Runtime* pRuntime,
                                      v8::Local<v8::Value> vp) {
-  return setPropertyInternal(pRuntime, vp, "Subject");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_title(CJS_Runtime* pRuntime) {
@@ -830,9 +832,8 @@
 
 CJS_Result CJS_Document::set_title(CJS_Runtime* pRuntime,
                                    v8::Local<v8::Value> vp) {
-  if (!m_pFormFillEnv)
-    return CJS_Result::Failure(JSMessage::kBadObjectError);
-  return setPropertyInternal(pRuntime, vp, "Title");
+  // Read-only.
+  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Document::get_num_pages(CJS_Runtime* pRuntime) {
@@ -935,9 +936,10 @@
     if (wsFilePath[i - 1] == L'\\' || wsFilePath[i - 1] == L'/')
       break;
   }
-  if (i > 0 && i < wsFilePath.GetLength())
-    return CJS_Result::Success(pRuntime->NewString(wsFilePath.c_str() + i));
-
+  if (i > 0 && i < wsFilePath.GetLength()) {
+    return CJS_Result::Success(
+        pRuntime->NewString(wsFilePath.AsStringView().Substr(i)));
+  }
   return CJS_Result::Success(pRuntime->NewString(""));
 }
 
@@ -1005,13 +1007,13 @@
 
   int nPageNo = pRuntime->ToInt32(params[0]);
   WideString swAnnotName = pRuntime->ToWideString(params[1]);
-  CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(nPageNo);
+  CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageViewAtIndex(nPageNo);
   if (!pPageView)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDFSDK_AnnotIteration annotIteration(pPageView, false);
+  CPDFSDK_AnnotIteration annot_iteration(pPageView);
   CPDFSDK_BAAnnot* pSDKBAAnnot = nullptr;
-  for (const auto& pSDKAnnotCur : annotIteration) {
+  for (const auto& pSDKAnnotCur : annot_iteration) {
     auto* pBAAnnot = pSDKAnnotCur->AsBAAnnot();
     if (pBAAnnot && pBAAnnot->GetAnnotName() == swAnnotName) {
       pSDKBAAnnot = pBAAnnot;
@@ -1026,8 +1028,8 @@
   if (pObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJS_Annot =
-      static_cast<CJS_Annot*>(CFXJS_Engine::GetObjectPrivate(pObj));
+  auto* pJS_Annot = static_cast<CJS_Annot*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
   if (!pJS_Annot)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1047,12 +1049,12 @@
   int nPageNo = m_pFormFillEnv->GetPageCount();
   v8::Local<v8::Array> annots = pRuntime->NewArray();
   for (int i = 0; i < nPageNo; ++i) {
-    CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(i);
+    CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageViewAtIndex(i);
     if (!pPageView)
       return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    CPDFSDK_AnnotIteration annotIteration(pPageView, false);
-    for (const auto& pSDKAnnotCur : annotIteration) {
+    CPDFSDK_AnnotIteration annot_iteration(pPageView);
+    for (const auto& pSDKAnnotCur : annot_iteration) {
       if (!pSDKAnnotCur)
         return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1061,8 +1063,8 @@
       if (pObj.IsEmpty())
         return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-      auto* pJS_Annot =
-          static_cast<CJS_Annot*>(CFXJS_Engine::GetObjectPrivate(pObj));
+      auto* pJS_Annot = static_cast<CJS_Annot*>(
+          CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
       pJS_Annot->SetSDKAnnot(pSDKAnnotCur->AsBAAnnot());
       pRuntime->PutArrayElement(
           annots, i,
@@ -1107,7 +1109,7 @@
     return CJS_Result::Failure(JSMessage::kTypeError);
 
   v8::Local<v8::Object> pObj = pRuntime->ToObject(params[1]);
-  if (!JSGetObject<CJS_Icon>(pObj))
+  if (!JSGetObject<CJS_Icon>(pRuntime->GetIsolate(), pObj))
     return CJS_Result::Failure(JSMessage::kTypeError);
 
   WideString swIconName = pRuntime->ToWideString(params[0]);
@@ -1129,8 +1131,8 @@
     if (pObj.IsEmpty())
       return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    auto* pJS_Icon =
-        static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+    auto* pJS_Icon = static_cast<CJS_Icon*>(
+        CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
     pJS_Icon->SetIconName(name);
     pRuntime->PutArrayElement(Icons, i++,
                               pJS_Icon
@@ -1161,7 +1163,8 @@
   if (pObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJSIcon = static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+  auto* pJSIcon = static_cast<CJS_Icon*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
   if (!pJSIcon)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1198,9 +1201,10 @@
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
-        m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM))) {
+  if (!m_pFormFillEnv->HasPermissions(
+          pdfium::access_permissions::kModifyContent |
+          pdfium::access_permissions::kModifyAnnotation |
+          pdfium::access_permissions::kFillForm)) {
     return CJS_Result::Failure(JSMessage::kPermissionError);
   }
 
@@ -1222,7 +1226,9 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
+
+  using pdfium::access_permissions::kExtractForAccessibility;
+  if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility))
     return CJS_Result::Failure(JSMessage::kPermissionError);
 
   // TODO(tsepez): check maximum allowable params.
@@ -1235,12 +1241,13 @@
   if (nPageNo < 0 || nPageNo >= pDocument->GetPageCount())
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(nPageNo);
+  RetainPtr<CPDF_Dictionary> pPageDict =
+      pDocument->GetMutablePageDictionary(nPageNo);
   if (!pPageDict)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, pPageDict);
-  page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
+  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, std::move(pPageDict));
+  page->AddPageImageCache();
   page->ParseContent();
 
   int nWords = 0;
@@ -1267,8 +1274,11 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
+
+  using pdfium::access_permissions::kExtractForAccessibility;
+  if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
+
   return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
@@ -1277,7 +1287,9 @@
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!m_pFormFillEnv)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
-  if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS))
+
+  using pdfium::access_permissions::kExtractForAccessibility;
+  if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility))
     return CJS_Result::Failure(JSMessage::kPermissionError);
 
   int nPageNo = params.size() > 0 ? pRuntime->ToInt32(params[0]) : 0;
@@ -1285,12 +1297,13 @@
   if (nPageNo < 0 || nPageNo >= pDocument->GetPageCount())
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(nPageNo);
+  RetainPtr<CPDF_Dictionary> pPageDict =
+      pDocument->GetMutablePageDictionary(nPageNo);
   if (!pPageDict)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, pPageDict);
-  page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
+  auto page = pdfium::MakeRetain<CPDF_Page>(pDocument, std::move(pPageDict));
+  page->AddPageImageCache();
   page->ParseContent();
 
   int nWords = 0;
@@ -1370,23 +1383,16 @@
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument();
-  CPDF_NameTree nameTree(pDocument, "Dests");
-  CPDF_Array* destArray =
-      nameTree.LookupNamedDest(pDocument, pRuntime->ToWideString(params[0]));
-  if (!destArray)
+  RetainPtr<const CPDF_Array> dest_array = CPDF_NameTree::LookupNamedDest(
+      pDocument, pRuntime->ToByteString(params[0]));
+  if (!dest_array)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  CPDF_Dest dest(destArray);
-  const CPDF_Array* arrayObject = dest.GetArray();
-  std::vector<float> scrollPositionArray;
-  if (arrayObject) {
-    for (size_t i = 2; i < arrayObject->size(); i++)
-      scrollPositionArray.push_back(arrayObject->GetNumberAt(i));
-  }
+  CPDF_Dest dest(std::move(dest_array));
+  std::vector<float> positions = dest.GetScrollPositionArray();
   pRuntime->BeginBlock();
   m_pFormFillEnv->DoGoToAction(dest.GetDestPageIndex(pDocument),
-                               dest.GetZoomMode(), scrollPositionArray.data(),
-                               scrollPositionArray.size());
+                               dest.GetZoomMode(), positions);
   pRuntime->EndBlock();
   return CJS_Result::Success();
 }
diff --git a/fxjs/cjs_document.h b/fxjs/cjs_document.h
index 49448fc..d410a5e 100644
--- a/fxjs/cjs_document.h
+++ b/fxjs/cjs_document.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,12 +17,11 @@
 
 class CPDFSDK_InteractiveForm;
 class CPDF_InteractiveForm;
-class CPDF_TextObject;
 struct CJS_DelayData;
 
 class CJS_Document final : public CJS_Object, public Observable {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Document(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -112,7 +111,7 @@
   JS_STATIC_METHOD(syncAnnotScan, CJS_Document)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
@@ -303,9 +302,6 @@
 
   CJS_Result getPropertyInternal(CJS_Runtime* pRuntime,
                                  const ByteString& propName);
-  CJS_Result setPropertyInternal(CJS_Runtime* pRuntime,
-                                 v8::Local<v8::Value> vp,
-                                 const ByteString& propName);
 
   CPDF_InteractiveForm* GetCoreInteractiveForm();
   CPDFSDK_InteractiveForm* GetSDKInteractiveForm();
diff --git a/fxjs/cjs_event.cpp b/fxjs/cjs_event.cpp
index a16ee6c..6b89038 100644
--- a/fxjs/cjs_event.cpp
+++ b/fxjs/cjs_event.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include "fxjs/cjs_event.h"
 
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
@@ -34,11 +33,11 @@
     {"value", get_value_static, set_value_static},
     {"willCommit", get_will_commit_static, set_will_commit_static}};
 
-int CJS_Event::ObjDefnID = -1;
+uint32_t CJS_Event::ObjDefnID = 0;
 const char CJS_Event::kName[] = "event";
 
 // static
-int CJS_Event::GetObjDefnID() {
+uint32_t CJS_Event::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -55,27 +54,22 @@
 CJS_Event::~CJS_Event() = default;
 
 CJS_Result CJS_Event::get_change(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(
       pRuntime->NewString(pEvent->Change().AsStringView()));
 }
 
 CJS_Result CJS_Event::set_change(CJS_Runtime* pRuntime,
                                  v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
-
   if (vp->IsString()) {
-    WideString& wChange = pEvent->Change();
-    wChange = pRuntime->ToWideString(vp);
+    CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
+    pEvent->Change() = pRuntime->ToWideString(vp);
   }
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Event::get_change_ex(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(
       pRuntime->NewString(pEvent->ChangeEx().AsStringView()));
 }
@@ -86,8 +80,7 @@
 }
 
 CJS_Result CJS_Event::get_commit_key(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewNumber(pEvent->CommitKey()));
 }
 
@@ -97,8 +90,7 @@
 }
 
 CJS_Result CJS_Event::get_field_full(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() != "Keystroke")
     return CJS_Result::Failure(L"unrecognized event");
 
@@ -111,8 +103,7 @@
 }
 
 CJS_Result CJS_Event::get_key_down(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->KeyDown()));
 }
 
@@ -122,8 +113,7 @@
 }
 
 CJS_Result CJS_Event::get_modifier(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Modifier()));
 }
 
@@ -133,8 +123,7 @@
 }
 
 CJS_Result CJS_Event::get_name(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewString(pEvent->Name()));
 }
 
@@ -143,14 +132,12 @@
 }
 
 CJS_Result CJS_Event::get_rc(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Rc()));
 }
 
 CJS_Result CJS_Event::set_rc(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   pEvent->Rc() = pRuntime->ToBoolean(vp);
   return CJS_Result::Success();
 }
@@ -183,8 +170,7 @@
 }
 
 CJS_Result CJS_Event::get_sel_end(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() != "Keystroke")
     return CJS_Result::Success();
 
@@ -193,8 +179,7 @@
 
 CJS_Result CJS_Event::set_sel_end(CJS_Runtime* pRuntime,
                                   v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() == "Keystroke")
     pEvent->SetSelEnd(pRuntime->ToInt32(vp));
 
@@ -202,8 +187,7 @@
 }
 
 CJS_Result CJS_Event::get_sel_start(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() != "Keystroke")
     return CJS_Result::Success();
 
@@ -212,8 +196,7 @@
 
 CJS_Result CJS_Event::set_sel_start(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Name() == "Keystroke")
     pEvent->SetSelStart(pRuntime->ToInt32(vp));
 
@@ -221,8 +204,7 @@
 }
 
 CJS_Result CJS_Event::get_shift(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Shift()));
 }
 
@@ -256,8 +238,7 @@
 }
 
 CJS_Result CJS_Event::get_target_name(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(
       pRuntime->NewString(pEvent->TargetName().AsStringView()));
 }
@@ -268,8 +249,7 @@
 }
 
 CJS_Result CJS_Event::get_type(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   return CJS_Result::Success(pRuntime->NewString(pEvent->Type()));
 }
 
@@ -278,8 +258,7 @@
 }
 
 CJS_Result CJS_Event::get_value(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Type() != "Field")
     return CJS_Result::Failure(L"Bad event type.");
 
@@ -292,8 +271,7 @@
 
 CJS_Result CJS_Event::set_value(CJS_Runtime* pRuntime,
                                 v8::Local<v8::Value> vp) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (pEvent->Type() != "Field")
     return CJS_Result::Failure(L"Bad event type.");
 
@@ -311,8 +289,7 @@
 }
 
 CJS_Result CJS_Event::get_will_commit(CJS_Runtime* pRuntime) {
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
 
   return CJS_Result::Success(pRuntime->NewBoolean(pEvent->WillCommit()));
 }
diff --git a/fxjs/cjs_event.h b/fxjs/cjs_event.h
index b2fa4dc..fc9bec2 100644
--- a/fxjs/cjs_event.h
+++ b/fxjs/cjs_event.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
 
 class CJS_Event final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Event(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -40,7 +40,7 @@
   JS_STATIC_PROP(willCommit, will_commit, CJS_Event)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
 
diff --git a/fxjs/cjs_event_context.cpp b/fxjs/cjs_event_context.cpp
index 27e8120..65eee9d 100644
--- a/fxjs/cjs_event_context.cpp
+++ b/fxjs/cjs_event_context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,27 +6,23 @@
 
 #include "fxjs/cjs_event_context.h"
 
+#include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fxcrt/autorestorer.h"
-#include "fxjs/cjs_eventrecorder.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
 
 CJS_EventContext::CJS_EventContext(CJS_Runtime* pRuntime)
-    : m_pRuntime(pRuntime),
-      m_pEventRecorder(pdfium::MakeUnique<CJS_EventRecorder>()) {
-  ASSERT(pRuntime);
-}
+    : m_pRuntime(pRuntime), m_pFormFillEnv(pRuntime->GetFormFillEnv()) {}
 
 CJS_EventContext::~CJS_EventContext() = default;
 
-CPDFSDK_FormFillEnvironment* CJS_EventContext::GetFormFillEnv() {
-  return m_pRuntime->GetFormFillEnv();
-}
-
-Optional<IJS_Runtime::JS_Error> CJS_EventContext::RunScript(
+absl::optional<IJS_Runtime::JS_Error> CJS_EventContext::RunScript(
     const WideString& script) {
   v8::Isolate::Scope isolate_scope(m_pRuntime->GetIsolate());
   v8::HandleScope handle_scope(m_pRuntime->GetIsolate());
@@ -41,20 +37,19 @@
   AutoRestorer<bool> restorer(&m_bBusy);
   m_bBusy = true;
 
-  ASSERT(m_pEventRecorder->IsValid());
-  CJS_Runtime::FieldEvent event(m_pEventRecorder->TargetName(),
-                                m_pEventRecorder->EventType());
+  DCHECK(IsValid());
+  CJS_Runtime::FieldEvent event(TargetName(), EventKind());
   if (!m_pRuntime->AddEventToSet(event)) {
     return IJS_Runtime::JS_Error(
         1, 1, JSGetStringFromID(JSMessage::kDuplicateEventError));
   }
 
-  Optional<IJS_Runtime::JS_Error> err;
+  absl::optional<IJS_Runtime::JS_Error> err;
   if (script.GetLength() > 0)
     err = m_pRuntime->ExecuteScript(script);
 
   m_pRuntime->RemoveEventFromSet(event);
-  m_pEventRecorder->Destroy();
+  Destroy();
   return err;
 }
 
@@ -69,17 +64,14 @@
   if (pFieldObj.IsEmpty())
     return nullptr;
 
-  auto* pFormFillEnv = m_pEventRecorder->GetFormFillEnvironment();
-  if (!pFormFillEnv)
-    pFormFillEnv = GetFormFillEnv();
-
-  auto* pJSDocument =
-      static_cast<CJS_Document*>(CFXJS_Engine::GetObjectPrivate(pDocObj));
+  auto* pFormFillEnv = GetFormFillEnv();
+  auto* pJSDocument = static_cast<CJS_Document*>(
+      CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pDocObj));
   pJSDocument->SetFormFillEnv(pFormFillEnv);
 
-  auto* pJSField =
-      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
-  pJSField->AttachField(pJSDocument, m_pEventRecorder->SourceName());
+  auto* pJSField = static_cast<CJS_Field*>(
+      CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pFieldObj));
+  pJSField->AttachField(pJSDocument, SourceName());
   return pJSField;
 }
 
@@ -94,137 +86,149 @@
   if (pFieldObj.IsEmpty())
     return nullptr;
 
-  auto* pFormFillEnv = m_pEventRecorder->GetFormFillEnvironment();
-  if (!pFormFillEnv)
-    pFormFillEnv = GetFormFillEnv();
-
-  auto* pJSDocument =
-      static_cast<CJS_Document*>(CFXJS_Engine::GetObjectPrivate(pDocObj));
+  auto* pFormFillEnv = GetFormFillEnv();
+  auto* pJSDocument = static_cast<CJS_Document*>(
+      CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pDocObj));
   pJSDocument->SetFormFillEnv(pFormFillEnv);
 
-  auto* pJSField =
-      static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pFieldObj));
-  pJSField->AttachField(pJSDocument, m_pEventRecorder->TargetName());
+  auto* pJSField = static_cast<CJS_Field*>(
+      CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pFieldObj));
+  pJSField->AttachField(pJSDocument, TargetName());
   return pJSField;
 }
 
-void CJS_EventContext::OnApp_Init() {
-  m_pEventRecorder->OnApp_Init();
+void CJS_EventContext::OnDoc_Open(const WideString& strTargetName) {
+  Initialize(Kind::kDocOpen);
+  m_strTargetName = strTargetName;
 }
 
-void CJS_EventContext::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                  const WideString& strTargetName) {
-  m_pEventRecorder->OnDoc_Open(pFormFillEnv, strTargetName);
+void CJS_EventContext::OnDoc_WillPrint() {
+  Initialize(Kind::kDocWillPrint);
 }
 
-void CJS_EventContext::OnDoc_WillPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_WillPrint(pFormFillEnv);
+void CJS_EventContext::OnDoc_DidPrint() {
+  Initialize(Kind::kDocDidPrint);
 }
 
-void CJS_EventContext::OnDoc_DidPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_DidPrint(pFormFillEnv);
+void CJS_EventContext::OnDoc_WillSave() {
+  Initialize(Kind::kDocWillSave);
 }
 
-void CJS_EventContext::OnDoc_WillSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_WillSave(pFormFillEnv);
+void CJS_EventContext::OnDoc_DidSave() {
+  Initialize(Kind::kDocDidSave);
 }
 
-void CJS_EventContext::OnDoc_DidSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_DidSave(pFormFillEnv);
+void CJS_EventContext::OnDoc_WillClose() {
+  Initialize(Kind::kDocWillClose);
 }
 
-void CJS_EventContext::OnDoc_WillClose(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnDoc_WillClose(pFormFillEnv);
+void CJS_EventContext::OnPage_Open() {
+  Initialize(Kind::kPageOpen);
 }
 
-void CJS_EventContext::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnPage_Open(pFormFillEnv);
+void CJS_EventContext::OnPage_Close() {
+  Initialize(Kind::kPageClose);
 }
 
-void CJS_EventContext::OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnPage_Close(pFormFillEnv);
+void CJS_EventContext::OnPage_InView() {
+  Initialize(Kind::kPageInView);
 }
 
-void CJS_EventContext::OnPage_InView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnPage_InView(pFormFillEnv);
-}
-
-void CJS_EventContext::OnPage_OutView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnPage_OutView(pFormFillEnv);
-}
-
-void CJS_EventContext::OnField_MouseDown(bool bModifier,
-                                         bool bShift,
-                                         CPDF_FormField* pTarget) {
-  m_pEventRecorder->OnField_MouseDown(bModifier, bShift, pTarget);
+void CJS_EventContext::OnPage_OutView() {
+  Initialize(Kind::kPageOutView);
 }
 
 void CJS_EventContext::OnField_MouseEnter(bool bModifier,
                                           bool bShift,
                                           CPDF_FormField* pTarget) {
-  m_pEventRecorder->OnField_MouseEnter(bModifier, bShift, pTarget);
+  Initialize(Kind::kFieldMouseEnter);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
 }
 
 void CJS_EventContext::OnField_MouseExit(bool bModifier,
                                          bool bShift,
                                          CPDF_FormField* pTarget) {
-  m_pEventRecorder->OnField_MouseExit(bModifier, bShift, pTarget);
+  Initialize(Kind::kFieldMouseExit);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+}
+
+void CJS_EventContext::OnField_MouseDown(bool bModifier,
+                                         bool bShift,
+                                         CPDF_FormField* pTarget) {
+  Initialize(Kind::kFieldMouseDown);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
 }
 
 void CJS_EventContext::OnField_MouseUp(bool bModifier,
                                        bool bShift,
                                        CPDF_FormField* pTarget) {
-  m_pEventRecorder->OnField_MouseUp(bModifier, bShift, pTarget);
+  Initialize(Kind::kFieldMouseUp);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
 }
 
 void CJS_EventContext::OnField_Focus(bool bModifier,
                                      bool bShift,
                                      CPDF_FormField* pTarget,
-                                     WideString* Value) {
-  m_pEventRecorder->OnField_Focus(bModifier, bShift, pTarget, Value);
+                                     WideString* pValue) {
+  DCHECK(pValue);
+  Initialize(Kind::kFieldFocus);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
 }
 
 void CJS_EventContext::OnField_Blur(bool bModifier,
                                     bool bShift,
                                     CPDF_FormField* pTarget,
-                                    WideString* Value) {
-  m_pEventRecorder->OnField_Blur(bModifier, bShift, pTarget, Value);
-}
-
-void CJS_EventContext::OnField_Calculate(CPDF_FormField* pSource,
-                                         CPDF_FormField* pTarget,
-                                         WideString* pValue,
-                                         bool* pRc) {
-  m_pEventRecorder->OnField_Calculate(pSource, pTarget, pValue, pRc);
-}
-
-void CJS_EventContext::OnField_Format(CPDF_FormField* pTarget,
-                                      WideString* Value) {
-  m_pEventRecorder->OnField_Format(pTarget, Value);
+                                    WideString* pValue) {
+  DCHECK(pValue);
+  Initialize(Kind::kFieldBlur);
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
 }
 
 void CJS_EventContext::OnField_Keystroke(WideString* strChange,
                                          const WideString& strChangeEx,
-                                         bool bKeyDown,
+                                         bool KeyDown,
                                          bool bModifier,
-                                         int* nSelEnd,
-                                         int* nSelStart,
+                                         int* pSelEnd,
+                                         int* pSelStart,
                                          bool bShift,
                                          CPDF_FormField* pTarget,
-                                         WideString* Value,
+                                         WideString* pValue,
                                          bool bWillCommit,
                                          bool bFieldFull,
-                                         bool* bRc) {
-  m_pEventRecorder->OnField_Keystroke(
-      strChange, strChangeEx, bKeyDown, bModifier, nSelEnd, nSelStart, bShift,
-      pTarget, Value, bWillCommit, bFieldFull, bRc);
+                                         bool* pbRc) {
+  DCHECK(pValue);
+  DCHECK(pbRc);
+  DCHECK(pSelStart);
+  DCHECK(pSelEnd);
+
+  Initialize(Kind::kFieldKeystroke);
+  m_nCommitKey = 0;
+  m_pWideStrChange = strChange;
+  m_WideStrChangeEx = strChangeEx;
+  m_bKeyDown = KeyDown;
+  m_bModifier = bModifier;
+  m_pISelEnd = pSelEnd;
+  m_pISelStart = pSelStart;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_bWillCommit = bWillCommit;
+  m_pbRc = pbRc;
+  m_bFieldFull = bFieldFull;
 }
 
 void CJS_EventContext::OnField_Validate(WideString* strChange,
@@ -233,94 +237,190 @@
                                         bool bModifier,
                                         bool bShift,
                                         CPDF_FormField* pTarget,
-                                        WideString* Value,
-                                        bool* bRc) {
-  m_pEventRecorder->OnField_Validate(strChange, strChangeEx, bKeyDown,
-                                     bModifier, bShift, pTarget, Value, bRc);
+                                        WideString* pValue,
+                                        bool* pbRc) {
+  DCHECK(pValue);
+  DCHECK(pbRc);
+  Initialize(Kind::kFieldValidate);
+  m_pWideStrChange = strChange;
+  m_WideStrChangeEx = strChangeEx;
+  m_bKeyDown = bKeyDown;
+  m_bModifier = bModifier;
+  m_bShift = bShift;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_pbRc = pbRc;
 }
 
-void CJS_EventContext::OnScreen_Focus(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_Focus(bModifier, bShift, pScreen);
+void CJS_EventContext::OnField_Calculate(CPDF_FormField* pSource,
+                                         CPDF_FormField* pTarget,
+                                         WideString* pValue,
+                                         bool* pRc) {
+  DCHECK(pValue);
+  DCHECK(pRc);
+  Initialize(Kind::kFieldCalculate);
+  if (pSource)
+    m_strSourceName = pSource->GetFullName();
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_pbRc = pRc;
 }
 
-void CJS_EventContext::OnScreen_Blur(bool bModifier,
-                                     bool bShift,
-                                     CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_Blur(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_Open(bool bModifier,
-                                     bool bShift,
-                                     CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_Open(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_Close(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_Close(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_MouseDown(bool bModifier,
-                                          bool bShift,
-                                          CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_MouseDown(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_MouseUp(bool bModifier,
-                                        bool bShift,
-                                        CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_MouseUp(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_MouseEnter(bool bModifier,
-                                           bool bShift,
-                                           CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_MouseEnter(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_MouseExit(bool bModifier,
-                                          bool bShift,
-                                          CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_MouseExit(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_InView(bool bModifier,
-                                       bool bShift,
-                                       CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_InView(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnScreen_OutView(bool bModifier,
-                                        bool bShift,
-                                        CPDFSDK_Annot* pScreen) {
-  m_pEventRecorder->OnScreen_OutView(bModifier, bShift, pScreen);
-}
-
-void CJS_EventContext::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) {
-  m_pEventRecorder->OnBookmark_MouseUp(pBookMark);
-}
-
-void CJS_EventContext::OnLink_MouseUp(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnLink_MouseUp(pFormFillEnv);
-}
-
-void CJS_EventContext::OnConsole_Exec() {
-  m_pEventRecorder->OnConsole_Exec();
+void CJS_EventContext::OnField_Format(CPDF_FormField* pTarget,
+                                      WideString* pValue) {
+  DCHECK(pValue);
+  Initialize(Kind::kFieldFormat);
+  m_nCommitKey = 0;
+  m_strTargetName = pTarget->GetFullName();
+  m_pValue = pValue;
+  m_bWillCommit = true;
 }
 
 void CJS_EventContext::OnExternal_Exec() {
-  m_pEventRecorder->OnExternal_Exec();
+  Initialize(Kind::kExternalExec);
 }
 
-void CJS_EventContext::OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  m_pEventRecorder->OnBatchExec(pFormFillEnv);
+void CJS_EventContext::Initialize(Kind kind) {
+  m_eKind = kind;
+  m_strTargetName.clear();
+  m_strSourceName.clear();
+  m_pWideStrChange = nullptr;
+  m_WideStrChangeDu.clear();
+  m_WideStrChangeEx.clear();
+  m_nCommitKey = -1;
+  m_bKeyDown = false;
+  m_bModifier = false;
+  m_bShift = false;
+  m_pISelEnd = nullptr;
+  m_nSelEndDu = 0;
+  m_pISelStart = nullptr;
+  m_nSelStartDu = 0;
+  m_bWillCommit = false;
+  m_pValue = nullptr;
+  m_bFieldFull = false;
+  m_pbRc = nullptr;
+  m_bRcDu = false;
+  m_bValid = true;
 }
 
-void CJS_EventContext::OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                   const WideString& strTargetName) {
-  m_pEventRecorder->OnMenu_Exec(pFormFillEnv, strTargetName);
+void CJS_EventContext::Destroy() {
+  m_bValid = false;
+}
+
+bool CJS_EventContext::IsUserGesture() const {
+  switch (m_eKind) {
+    case Kind::kFieldMouseDown:
+    case Kind::kFieldMouseUp:
+    case Kind::kFieldKeystroke:
+      return true;
+    default:
+      return false;
+  }
+}
+
+WideString& CJS_EventContext::Change() {
+  return m_pWideStrChange ? *m_pWideStrChange : m_WideStrChangeDu;
+}
+
+ByteStringView CJS_EventContext::Name() const {
+  switch (m_eKind) {
+    case Kind::kDocDidPrint:
+      return "DidPrint";
+    case Kind::kDocDidSave:
+      return "DidSave";
+    case Kind::kDocOpen:
+      return "Open";
+    case Kind::kDocWillClose:
+      return "WillClose";
+    case Kind::kDocWillPrint:
+      return "WillPrint";
+    case Kind::kDocWillSave:
+      return "WillSave";
+    case Kind::kExternalExec:
+      return "Exec";
+    case Kind::kFieldFocus:
+      return "Focus";
+    case Kind::kFieldBlur:
+      return "Blur";
+    case Kind::kFieldMouseDown:
+      return "Mouse Down";
+    case Kind::kFieldMouseUp:
+      return "Mouse Up";
+    case Kind::kFieldMouseEnter:
+      return "Mouse Enter";
+    case Kind::kFieldMouseExit:
+      return "Mouse Exit";
+    case Kind::kFieldCalculate:
+      return "Calculate";
+    case Kind::kFieldFormat:
+      return "Format";
+    case Kind::kFieldKeystroke:
+      return "Keystroke";
+    case Kind::kFieldValidate:
+      return "Validate";
+    case Kind::kPageOpen:
+      return "Open";
+    case Kind::kPageClose:
+      return "Close";
+    case Kind::kPageInView:
+      return "InView";
+    case Kind::kPageOutView:
+      return "OutView";
+    default:
+      return "";
+  }
+}
+
+ByteStringView CJS_EventContext::Type() const {
+  switch (m_eKind) {
+    case Kind::kDocDidPrint:
+    case Kind::kDocDidSave:
+    case Kind::kDocOpen:
+    case Kind::kDocWillClose:
+    case Kind::kDocWillPrint:
+    case Kind::kDocWillSave:
+      return "Doc";
+    case Kind::kExternalExec:
+      return "External";
+    case Kind::kFieldBlur:
+    case Kind::kFieldFocus:
+    case Kind::kFieldMouseDown:
+    case Kind::kFieldMouseUp:
+    case Kind::kFieldMouseEnter:
+    case Kind::kFieldMouseExit:
+    case Kind::kFieldCalculate:
+    case Kind::kFieldFormat:
+    case Kind::kFieldKeystroke:
+    case Kind::kFieldValidate:
+      return "Field";
+    case Kind::kPageOpen:
+    case Kind::kPageClose:
+    case Kind::kPageInView:
+    case Kind::kPageOutView:
+      return "Page";
+    default:
+      return "";
+  }
+}
+
+bool& CJS_EventContext::Rc() {
+  return m_pbRc ? *m_pbRc : m_bRcDu;
+}
+
+int CJS_EventContext::SelEnd() const {
+  return m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
+}
+
+int CJS_EventContext::SelStart() const {
+  return m_pISelStart ? *m_pISelStart : m_nSelStartDu;
+}
+
+void CJS_EventContext::SetSelEnd(int value) {
+  int& target = m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
+  target = value;
+}
+
+void CJS_EventContext::SetSelStart(int value) {
+  int& target = m_pISelStart ? *m_pISelStart : m_nSelStartDu;
+  target = value;
 }
diff --git a/fxjs/cjs_event_context.h b/fxjs/cjs_event_context.h
index 6a953e6..a0b266b 100644
--- a/fxjs/cjs_event_context.h
+++ b/fxjs/cjs_event_context.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,37 +7,58 @@
 #ifndef FXJS_CJS_EVENT_CONTEXT_H_
 #define FXJS_CJS_EVENT_CONTEXT_H_
 
-#include <memory>
-
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fxjs/ijs_event_context.h"
 
-class CJS_EventRecorder;
 class CJS_Field;
 class CJS_Runtime;
-class CPDFSDK_FormFillEnvironment;
 
 class CJS_EventContext final : public IJS_EventContext {
  public:
+  enum class Kind : uint8_t {
+    kUnknown,
+    kDocOpen,
+    kDocWillPrint,
+    kDocDidPrint,
+    kDocWillSave,
+    kDocDidSave,
+    kDocWillClose,
+    kPageOpen,
+    kPageClose,
+    kPageInView,
+    kPageOutView,
+    kFieldMouseDown,
+    kFieldMouseUp,
+    kFieldMouseEnter,
+    kFieldMouseExit,
+    kFieldFocus,
+    kFieldBlur,
+    kFieldKeystroke,
+    kFieldValidate,
+    kFieldCalculate,
+    kFieldFormat,
+    kExternalExec,
+  };
+
   explicit CJS_EventContext(CJS_Runtime* pRuntime);
   ~CJS_EventContext() override;
 
   // IJS_EventContext
-  Optional<IJS_Runtime::JS_Error> RunScript(const WideString& script) override;
-  void OnApp_Init() override;
-  void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                  const WideString& strTargetName) override;
-  void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
+  absl::optional<IJS_Runtime::JS_Error> RunScript(
+      const WideString& script) override;
+  void OnDoc_Open(const WideString& strTargetName) override;
+  void OnDoc_WillPrint() override;
+  void OnDoc_DidPrint() override;
+  void OnDoc_WillSave() override;
+  void OnDoc_DidSave() override;
+  void OnDoc_WillClose() override;
+  void OnPage_Open() override;
+  void OnPage_Close() override;
+  void OnPage_InView() override;
+  void OnPage_OutView() override;
   void OnField_MouseDown(bool bModifier,
                          bool bShift,
                          CPDF_FormField* pTarget) override;
@@ -83,54 +104,72 @@
                         CPDF_FormField* pTarget,
                         WideString* Value,
                         bool* bRc) override;
-  void OnScreen_Focus(bool bModifier,
-                      bool bShift,
-                      CPDFSDK_Annot* pScreen) override;
-  void OnScreen_Blur(bool bModifier,
-                     bool bShift,
-                     CPDFSDK_Annot* pScreen) override;
-  void OnScreen_Open(bool bModifier,
-                     bool bShift,
-                     CPDFSDK_Annot* pScreen) override;
-  void OnScreen_Close(bool bModifier,
-                      bool bShift,
-                      CPDFSDK_Annot* pScreen) override;
-  void OnScreen_MouseDown(bool bModifier,
-                          bool bShift,
-                          CPDFSDK_Annot* pScreen) override;
-  void OnScreen_MouseUp(bool bModifier,
-                        bool bShift,
-                        CPDFSDK_Annot* pScreen) override;
-  void OnScreen_MouseEnter(bool bModifier,
-                           bool bShift,
-                           CPDFSDK_Annot* pScreen) override;
-  void OnScreen_MouseExit(bool bModifier,
-                          bool bShift,
-                          CPDFSDK_Annot* pScreen) override;
-  void OnScreen_InView(bool bModifier,
-                       bool bShift,
-                       CPDFSDK_Annot* pScreen) override;
-  void OnScreen_OutView(bool bModifier,
-                        bool bShift,
-                        CPDFSDK_Annot* pScreen) override;
-  void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) override;
-  void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                   const WideString& strTargetName) override;
-  void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) override;
-  void OnConsole_Exec() override;
   void OnExternal_Exec() override;
 
-  CJS_Runtime* GetJSRuntime() const { return m_pRuntime.Get(); }
-  CJS_EventRecorder* GetEventRecorder() const { return m_pEventRecorder.get(); }
-  CPDFSDK_FormFillEnvironment* GetFormFillEnv();
+  CJS_Runtime* GetJSRuntime() const { return m_pRuntime; }
+  CPDFSDK_FormFillEnvironment* GetFormFillEnv() const {
+    return m_pFormFillEnv.Get();
+  }
   CJS_Field* SourceField();
   CJS_Field* TargetField();
 
+  Kind EventKind() const { return m_eKind; }
+  bool IsValid() const { return m_bValid; }
+  bool IsUserGesture() const;
+  WideString& Change();
+  WideString ChangeEx() const { return m_WideStrChangeEx; }
+  WideString SourceName() const { return m_strSourceName; }
+  WideString TargetName() const { return m_strTargetName; }
+  int CommitKey() const { return m_nCommitKey; }
+  bool FieldFull() const { return m_bFieldFull; }
+  bool KeyDown() const { return m_bKeyDown; }
+  bool Modifier() const { return m_bModifier; }
+  ByteStringView Name() const;
+  ByteStringView Type() const;
+  bool& Rc();
+  int SelEnd() const;
+  int SelStart() const;
+  void SetSelEnd(int value);
+  void SetSelStart(int value);
+  bool Shift() const { return m_bShift; }
+  bool HasValue() const { return !!m_pValue; }
+  WideString& Value() { return *m_pValue; }
+  bool WillCommit() const { return m_bWillCommit; }
+
+  void SetValueForTest(WideString* pStr) { m_pValue = pStr; }
+  void SetRCForTest(bool* pRC) { m_pbRc = pRC; }
+  void SetStrChangeForTest(WideString* pStrChange) {
+    m_pWideStrChange = pStrChange;
+  }
+  void ResetWillCommitForTest() { m_bWillCommit = false; }
+
  private:
+  void Initialize(Kind kind);
+  void Destroy();
+
   UnownedPtr<CJS_Runtime> const m_pRuntime;
-  std::unique_ptr<CJS_EventRecorder> m_pEventRecorder;
+  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+  Kind m_eKind = Kind::kUnknown;
   bool m_bBusy = false;
+  bool m_bValid = false;
+  UnownedPtr<WideString> m_pValue;
+  WideString m_strSourceName;
+  WideString m_strTargetName;
+  WideString m_WideStrChangeDu;
+  WideString m_WideStrChangeEx;
+  UnownedPtr<WideString> m_pWideStrChange;
+  int m_nCommitKey = -1;
+  bool m_bKeyDown = false;
+  bool m_bModifier = false;
+  bool m_bShift = false;
+  int m_nSelEndDu = 0;
+  int m_nSelStartDu = 0;
+  UnownedPtr<int> m_pISelEnd;
+  UnownedPtr<int> m_pISelStart;
+  bool m_bWillCommit = false;
+  bool m_bFieldFull = false;
+  bool m_bRcDu = false;
+  UnownedPtr<bool> m_pbRc;
 };
 
 #endif  // FXJS_CJS_EVENT_CONTEXT_H_
diff --git a/fxjs/cjs_event_context_stub.cpp b/fxjs/cjs_event_context_stub.cpp
index 82530e4..b8d0518 100644
--- a/fxjs/cjs_event_context_stub.cpp
+++ b/fxjs/cjs_event_context_stub.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,11 @@
 
 #include "fxjs/cjs_event_context_stub.h"
 
-Optional<IJS_Runtime::JS_Error> CJS_EventContextStub::RunScript(
+CJS_EventContextStub::CJS_EventContextStub() = default;
+
+CJS_EventContextStub::~CJS_EventContextStub() = default;
+
+absl::optional<IJS_Runtime::JS_Error> CJS_EventContextStub::RunScript(
     const WideString& script) {
   return IJS_Runtime::JS_Error(1, 1, L"JavaScript support not present");
 }
diff --git a/fxjs/cjs_event_context_stub.h b/fxjs/cjs_event_context_stub.h
index 06d0184..5c9b50a 100644
--- a/fxjs/cjs_event_context_stub.h
+++ b/fxjs/cjs_event_context_stub.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,24 +11,23 @@
 
 class CJS_EventContextStub final : public IJS_EventContext {
  public:
-  CJS_EventContextStub() {}
-  ~CJS_EventContextStub() override {}
+  CJS_EventContextStub();
+  ~CJS_EventContextStub() override;
 
   // IJS_EventContext:
-  Optional<IJS_Runtime::JS_Error> RunScript(const WideString& script) override;
+  absl::optional<IJS_Runtime::JS_Error> RunScript(
+      const WideString& script) override;
 
-  void OnApp_Init() override {}
-  void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                  const WideString& strTargetName) override {}
-  void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
+  void OnDoc_Open(const WideString& strTargetName) override {}
+  void OnDoc_WillPrint() override {}
+  void OnDoc_DidPrint() override {}
+  void OnDoc_WillSave() override {}
+  void OnDoc_DidSave() override {}
+  void OnDoc_WillClose() override {}
+  void OnPage_Open() override {}
+  void OnPage_Close() override {}
+  void OnPage_InView() override {}
+  void OnPage_OutView() override {}
   void OnField_MouseDown(bool bModifier,
                          bool bShift,
                          CPDF_FormField* pTarget) override {}
@@ -74,42 +73,6 @@
                         CPDF_FormField* pTarget,
                         WideString* Value,
                         bool* bRc) override {}
-  void OnScreen_Focus(bool bModifier,
-                      bool bShift,
-                      CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_Blur(bool bModifier,
-                     bool bShift,
-                     CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_Open(bool bModifier,
-                     bool bShift,
-                     CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_Close(bool bModifier,
-                      bool bShift,
-                      CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_MouseDown(bool bModifier,
-                          bool bShift,
-                          CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_MouseUp(bool bModifier,
-                        bool bShift,
-                        CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_MouseEnter(bool bModifier,
-                           bool bShift,
-                           CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_MouseExit(bool bModifier,
-                          bool bShift,
-                          CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_InView(bool bModifier,
-                       bool bShift,
-                       CPDFSDK_Annot* pScreen) override {}
-  void OnScreen_OutView(bool bModifier,
-                        bool bShift,
-                        CPDFSDK_Annot* pScreen) override {}
-  void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) override {}
-  void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                   const WideString&) override {}
-  void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {}
-  void OnConsole_Exec() override {}
   void OnExternal_Exec() override {}
 };
 
diff --git a/fxjs/cjs_eventrecorder.cpp b/fxjs/cjs_eventrecorder.cpp
deleted file mode 100644
index 59febf0..0000000
--- a/fxjs/cjs_eventrecorder.cpp
+++ /dev/null
@@ -1,558 +0,0 @@
-// Copyright 2014 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
-
-#include "fxjs/cjs_eventrecorder.h"
-
-#include "core/fpdfdoc/cpdf_bookmark.h"
-#include "core/fpdfdoc/cpdf_formfield.h"
-
-CJS_EventRecorder::CJS_EventRecorder() = default;
-
-CJS_EventRecorder::~CJS_EventRecorder() = default;
-
-void CJS_EventRecorder::OnApp_Init() {
-  Initialize(JET_APP_INIT);
-}
-
-void CJS_EventRecorder::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                                   const WideString& strTargetName) {
-  Initialize(JET_DOC_OPEN);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-  m_strTargetName = strTargetName;
-}
-
-void CJS_EventRecorder::OnDoc_WillPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_WILLPRINT);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnDoc_DidPrint(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_DIDPRINT);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnDoc_WillSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_WILLSAVE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnDoc_DidSave(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_DIDSAVE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnDoc_WillClose(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_DOC_WILLCLOSE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_PAGE_OPEN);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnPage_Close(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_PAGE_CLOSE);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnPage_InView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_PAGE_INVIEW);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnPage_OutView(
-    CPDFSDK_FormFillEnvironment* pFormFillEnv) {
-  Initialize(JET_PAGE_OUTVIEW);
-  m_pTargetFormFillEnv.Reset(pFormFillEnv);
-}
-
-void CJS_EventRecorder::OnField_MouseEnter(bool bModifier,
-                                           bool bShift,
-                                           CPDF_FormField* pTarget) {
-  Initialize(JET_FIELD_MOUSEENTER);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventRecorder::OnField_MouseExit(bool bModifier,
-                                          bool bShift,
-                                          CPDF_FormField* pTarget) {
-  Initialize(JET_FIELD_MOUSEEXIT);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventRecorder::OnField_MouseDown(bool bModifier,
-                                          bool bShift,
-                                          CPDF_FormField* pTarget) {
-  Initialize(JET_FIELD_MOUSEDOWN);
-  m_eEventType = JET_FIELD_MOUSEDOWN;
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventRecorder::OnField_MouseUp(bool bModifier,
-                                        bool bShift,
-                                        CPDF_FormField* pTarget) {
-  Initialize(JET_FIELD_MOUSEUP);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-}
-
-void CJS_EventRecorder::OnField_Focus(bool bModifier,
-                                      bool bShift,
-                                      CPDF_FormField* pTarget,
-                                      WideString* pValue) {
-  ASSERT(pValue);
-  Initialize(JET_FIELD_FOCUS);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-}
-
-void CJS_EventRecorder::OnField_Blur(bool bModifier,
-                                     bool bShift,
-                                     CPDF_FormField* pTarget,
-                                     WideString* pValue) {
-  ASSERT(pValue);
-  Initialize(JET_FIELD_BLUR);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-}
-
-void CJS_EventRecorder::OnField_Keystroke(WideString* strChange,
-                                          const WideString& strChangeEx,
-                                          bool KeyDown,
-                                          bool bModifier,
-                                          int* pSelEnd,
-                                          int* pSelStart,
-                                          bool bShift,
-                                          CPDF_FormField* pTarget,
-                                          WideString* pValue,
-                                          bool bWillCommit,
-                                          bool bFieldFull,
-                                          bool* pbRc) {
-  ASSERT(pValue);
-  ASSERT(pbRc);
-  ASSERT(pSelStart);
-  ASSERT(pSelEnd);
-
-  Initialize(JET_FIELD_KEYSTROKE);
-
-  m_nCommitKey = 0;
-  m_pWideStrChange = strChange;
-  m_WideStrChangeEx = strChangeEx;
-  m_bKeyDown = KeyDown;
-  m_bModifier = bModifier;
-  m_pISelEnd = pSelEnd;
-  m_pISelStart = pSelStart;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-  m_bWillCommit = bWillCommit;
-  m_pbRc = pbRc;
-  m_bFieldFull = bFieldFull;
-}
-
-void CJS_EventRecorder::OnField_Validate(WideString* strChange,
-                                         const WideString& strChangeEx,
-                                         bool bKeyDown,
-                                         bool bModifier,
-                                         bool bShift,
-                                         CPDF_FormField* pTarget,
-                                         WideString* pValue,
-                                         bool* pbRc) {
-  ASSERT(pValue);
-  ASSERT(pbRc);
-
-  Initialize(JET_FIELD_VALIDATE);
-
-  m_pWideStrChange = strChange;
-  m_WideStrChangeEx = strChangeEx;
-  m_bKeyDown = bKeyDown;
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-  m_pbRc = pbRc;
-}
-
-void CJS_EventRecorder::OnField_Calculate(CPDF_FormField* pSource,
-                                          CPDF_FormField* pTarget,
-                                          WideString* pValue,
-                                          bool* pRc) {
-  ASSERT(pValue);
-  ASSERT(pRc);
-
-  Initialize(JET_FIELD_CALCULATE);
-
-  if (pSource)
-    m_strSourceName = pSource->GetFullName();
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-  m_pbRc = pRc;
-}
-
-void CJS_EventRecorder::OnField_Format(CPDF_FormField* pTarget,
-                                       WideString* pValue) {
-  ASSERT(pValue);
-  Initialize(JET_FIELD_FORMAT);
-
-  m_nCommitKey = 0;
-  m_strTargetName = pTarget->GetFullName();
-  m_pValue = pValue;
-  m_bWillCommit = true;
-}
-
-void CJS_EventRecorder::OnScreen_Focus(bool bModifier,
-                                       bool bShift,
-                                       CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_FOCUS);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_Blur(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_BLUR);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_Open(bool bModifier,
-                                      bool bShift,
-                                      CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_OPEN);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_Close(bool bModifier,
-                                       bool bShift,
-                                       CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_CLOSE);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_MouseDown(bool bModifier,
-                                           bool bShift,
-                                           CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_MOUSEDOWN);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_MouseUp(bool bModifier,
-                                         bool bShift,
-                                         CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_MOUSEUP);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_MouseEnter(bool bModifier,
-                                            bool bShift,
-                                            CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_MOUSEENTER);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_MouseExit(bool bModifier,
-                                           bool bShift,
-                                           CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_MOUSEEXIT);
-
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_InView(bool bModifier,
-                                        bool bShift,
-                                        CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_INVIEW);
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnScreen_OutView(bool bModifier,
-                                         bool bShift,
-                                         CPDFSDK_Annot* pScreen) {
-  Initialize(JET_SCREEN_OUTVIEW);
-  m_bModifier = bModifier;
-  m_bShift = bShift;
-  m_pTargetAnnot.Reset(pScreen);
-}
-
-void CJS_EventRecorder::OnLink_MouseUp(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) {
-  Initialize(JET_LINK_MOUSEUP);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-}
-
-void CJS_EventRecorder::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) {
-  Initialize(JET_BOOKMARK_MOUSEUP);
-  m_pTargetBookMark = pBookMark;
-}
-
-void CJS_EventRecorder::OnMenu_Exec(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv,
-    const WideString& strTargetName) {
-  Initialize(JET_MENU_EXEC);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-  m_strTargetName = strTargetName;
-}
-
-void CJS_EventRecorder::OnExternal_Exec() {
-  Initialize(JET_EXTERNAL_EXEC);
-}
-
-void CJS_EventRecorder::OnBatchExec(
-    CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) {
-  Initialize(JET_BATCH_EXEC);
-  m_pTargetFormFillEnv.Reset(pTargetFormFillEnv);
-}
-
-void CJS_EventRecorder::OnConsole_Exec() {
-  Initialize(JET_CONSOLE_EXEC);
-}
-
-void CJS_EventRecorder::Initialize(JS_EVENT_T type) {
-  m_eEventType = type;
-  m_strTargetName.clear();
-  m_strSourceName.clear();
-  m_pWideStrChange = nullptr;
-  m_WideStrChangeDu.clear();
-  m_WideStrChangeEx.clear();
-  m_nCommitKey = -1;
-  m_bKeyDown = false;
-  m_bModifier = false;
-  m_bShift = false;
-  m_pISelEnd = nullptr;
-  m_nSelEndDu = 0;
-  m_pISelStart = nullptr;
-  m_nSelStartDu = 0;
-  m_bWillCommit = false;
-  m_pValue = nullptr;
-  m_bFieldFull = false;
-  m_pbRc = nullptr;
-  m_bRcDu = false;
-  m_pTargetBookMark = nullptr;
-  m_pTargetFormFillEnv.Reset();
-  m_pTargetAnnot.Reset();
-  m_bValid = true;
-}
-
-void CJS_EventRecorder::Destroy() {
-  m_bValid = false;
-}
-
-bool CJS_EventRecorder::IsUserGesture() const {
-  switch (m_eEventType) {
-    case JET_FIELD_MOUSEDOWN:
-    case JET_FIELD_MOUSEUP:
-    case JET_SCREEN_MOUSEDOWN:
-    case JET_SCREEN_MOUSEUP:
-    case JET_BOOKMARK_MOUSEUP:
-    case JET_LINK_MOUSEUP:
-    case JET_FIELD_KEYSTROKE:
-      return true;
-    default:
-      return false;
-  }
-}
-
-WideString& CJS_EventRecorder::Change() {
-  return m_pWideStrChange ? *m_pWideStrChange : m_WideStrChangeDu;
-}
-
-ByteStringView CJS_EventRecorder::Name() const {
-  switch (m_eEventType) {
-    case JET_APP_INIT:
-      return "Init";
-    case JET_BATCH_EXEC:
-      return "Exec";
-    case JET_BOOKMARK_MOUSEUP:
-      return "Mouse Up";
-    case JET_CONSOLE_EXEC:
-      return "Exec";
-    case JET_DOC_DIDPRINT:
-      return "DidPrint";
-    case JET_DOC_DIDSAVE:
-      return "DidSave";
-    case JET_DOC_OPEN:
-      return "Open";
-    case JET_DOC_WILLCLOSE:
-      return "WillClose";
-    case JET_DOC_WILLPRINT:
-      return "WillPrint";
-    case JET_DOC_WILLSAVE:
-      return "WillSave";
-    case JET_EXTERNAL_EXEC:
-      return "Exec";
-    case JET_FIELD_FOCUS:
-    case JET_SCREEN_FOCUS:
-      return "Focus";
-    case JET_FIELD_BLUR:
-    case JET_SCREEN_BLUR:
-      return "Blur";
-    case JET_FIELD_MOUSEDOWN:
-    case JET_SCREEN_MOUSEDOWN:
-      return "Mouse Down";
-    case JET_FIELD_MOUSEUP:
-    case JET_SCREEN_MOUSEUP:
-      return "Mouse Up";
-    case JET_FIELD_MOUSEENTER:
-    case JET_SCREEN_MOUSEENTER:
-      return "Mouse Enter";
-    case JET_FIELD_MOUSEEXIT:
-    case JET_SCREEN_MOUSEEXIT:
-      return "Mouse Exit";
-    case JET_FIELD_CALCULATE:
-      return "Calculate";
-    case JET_FIELD_FORMAT:
-      return "Format";
-    case JET_FIELD_KEYSTROKE:
-      return "Keystroke";
-    case JET_FIELD_VALIDATE:
-      return "Validate";
-    case JET_LINK_MOUSEUP:
-      return "Mouse Up";
-    case JET_MENU_EXEC:
-      return "Exec";
-    case JET_PAGE_OPEN:
-    case JET_SCREEN_OPEN:
-      return "Open";
-    case JET_PAGE_CLOSE:
-    case JET_SCREEN_CLOSE:
-      return "Close";
-    case JET_SCREEN_INVIEW:
-    case JET_PAGE_INVIEW:
-      return "InView";
-    case JET_PAGE_OUTVIEW:
-    case JET_SCREEN_OUTVIEW:
-      return "OutView";
-    default:
-      return "";
-  }
-}
-
-ByteStringView CJS_EventRecorder::Type() const {
-  switch (m_eEventType) {
-    case JET_APP_INIT:
-      return "App";
-    case JET_BATCH_EXEC:
-      return "Batch";
-    case JET_BOOKMARK_MOUSEUP:
-      return "BookMark";
-    case JET_CONSOLE_EXEC:
-      return "Console";
-    case JET_DOC_DIDPRINT:
-    case JET_DOC_DIDSAVE:
-    case JET_DOC_OPEN:
-    case JET_DOC_WILLCLOSE:
-    case JET_DOC_WILLPRINT:
-    case JET_DOC_WILLSAVE:
-      return "Doc";
-    case JET_EXTERNAL_EXEC:
-      return "External";
-    case JET_FIELD_BLUR:
-    case JET_FIELD_FOCUS:
-    case JET_FIELD_MOUSEDOWN:
-    case JET_FIELD_MOUSEENTER:
-    case JET_FIELD_MOUSEEXIT:
-    case JET_FIELD_MOUSEUP:
-    case JET_FIELD_CALCULATE:
-    case JET_FIELD_FORMAT:
-    case JET_FIELD_KEYSTROKE:
-    case JET_FIELD_VALIDATE:
-      return "Field";
-    case JET_SCREEN_FOCUS:
-    case JET_SCREEN_BLUR:
-    case JET_SCREEN_OPEN:
-    case JET_SCREEN_CLOSE:
-    case JET_SCREEN_MOUSEDOWN:
-    case JET_SCREEN_MOUSEUP:
-    case JET_SCREEN_MOUSEENTER:
-    case JET_SCREEN_MOUSEEXIT:
-    case JET_SCREEN_INVIEW:
-    case JET_SCREEN_OUTVIEW:
-      return "Screen";
-    case JET_LINK_MOUSEUP:
-      return "Link";
-    case JET_MENU_EXEC:
-      return "Menu";
-    case JET_PAGE_OPEN:
-    case JET_PAGE_CLOSE:
-    case JET_PAGE_INVIEW:
-    case JET_PAGE_OUTVIEW:
-      return "Page";
-    default:
-      return "";
-  }
-}
-
-bool& CJS_EventRecorder::Rc() {
-  return m_pbRc ? *m_pbRc : m_bRcDu;
-}
-
-int CJS_EventRecorder::SelEnd() const {
-  return m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
-}
-
-int CJS_EventRecorder::SelStart() const {
-  return m_pISelStart ? *m_pISelStart : m_nSelStartDu;
-}
-
-void CJS_EventRecorder::SetSelEnd(int value) {
-  int& target = m_pISelEnd ? *m_pISelEnd : m_nSelEndDu;
-  target = value;
-}
-
-void CJS_EventRecorder::SetSelStart(int value) {
-  int& target = m_pISelStart ? *m_pISelStart : m_nSelStartDu;
-  target = value;
-}
diff --git a/fxjs/cjs_eventrecorder.h b/fxjs/cjs_eventrecorder.h
deleted file mode 100644
index acf9856..0000000
--- a/fxjs/cjs_eventrecorder.h
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2014 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 FXJS_CJS_EVENTRECORDER_H_
-#define FXJS_CJS_EVENTRECORDER_H_
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "fpdfsdk/cpdfsdk_annot.h"
-#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
-
-class CPDF_Bookmark;
-class CPDF_FormField;
-
-enum JS_EVENT_T {
-  JET_UNKNOWN,
-  JET_APP_INIT,
-  JET_DOC_OPEN,
-  JET_DOC_WILLPRINT,
-  JET_DOC_DIDPRINT,
-  JET_DOC_WILLSAVE,
-  JET_DOC_DIDSAVE,
-  JET_DOC_WILLCLOSE,
-  JET_PAGE_OPEN,
-  JET_PAGE_CLOSE,
-  JET_PAGE_INVIEW,
-  JET_PAGE_OUTVIEW,
-  JET_FIELD_MOUSEDOWN,
-  JET_FIELD_MOUSEUP,
-  JET_FIELD_MOUSEENTER,
-  JET_FIELD_MOUSEEXIT,
-  JET_FIELD_FOCUS,
-  JET_FIELD_BLUR,
-  JET_FIELD_KEYSTROKE,
-  JET_FIELD_VALIDATE,
-  JET_FIELD_CALCULATE,
-  JET_FIELD_FORMAT,
-  JET_SCREEN_FOCUS,
-  JET_SCREEN_BLUR,
-  JET_SCREEN_OPEN,
-  JET_SCREEN_CLOSE,
-  JET_SCREEN_MOUSEDOWN,
-  JET_SCREEN_MOUSEUP,
-  JET_SCREEN_MOUSEENTER,
-  JET_SCREEN_MOUSEEXIT,
-  JET_SCREEN_INVIEW,
-  JET_SCREEN_OUTVIEW,
-  JET_BATCH_EXEC,
-  JET_MENU_EXEC,
-  JET_CONSOLE_EXEC,
-  JET_EXTERNAL_EXEC,
-  JET_BOOKMARK_MOUSEUP,
-  JET_LINK_MOUSEUP
-};
-
-class CJS_EventRecorder {
- public:
-  CJS_EventRecorder();
-  ~CJS_EventRecorder();
-
-  void OnApp_Init();
-
-  void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                  const WideString& strTargetName);
-  void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnField_Calculate(CPDF_FormField* pSource,
-                         CPDF_FormField* pTarget,
-                         WideString* Value,
-                         bool* pbRc);
-  void OnField_Format(CPDF_FormField* pTarget, WideString* Value);
-  void OnField_Keystroke(WideString* strChange,
-                         const WideString& strChangeEx,
-                         bool KeyDown,
-                         bool bModifier,
-                         int* nSelEnd,
-                         int* nSelStart,
-                         bool bShift,
-                         CPDF_FormField* pTarget,
-                         WideString* Value,
-                         bool bWillCommit,
-                         bool bFieldFull,
-                         bool* bRc);
-  void OnField_Validate(WideString* strChange,
-                        const WideString& strChangeEx,
-                        bool bKeyDown,
-                        bool bModifier,
-                        bool bShift,
-                        CPDF_FormField* pTarget,
-                        WideString* Value,
-                        bool* bRc);
-  void OnField_MouseDown(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseEnter(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseExit(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_MouseUp(bool bModifier, bool bShift, CPDF_FormField* pTarget);
-  void OnField_Blur(bool bModifier,
-                    bool bShift,
-                    CPDF_FormField* pTarget,
-                    WideString* Value);
-  void OnField_Focus(bool bModifier,
-                     bool bShift,
-                     CPDF_FormField* pTarget,
-                     WideString* Value);
-
-  void OnScreen_Focus(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Blur(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Open(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_Close(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseDown(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseUp(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseEnter(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_MouseExit(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_InView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-  void OnScreen_OutView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen);
-
-  void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark);
-  void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
-  void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                   const WideString& strTargetName);
-  void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  void OnConsole_Exec();
-  void OnExternal_Exec();
-
-  void Destroy();
-
-  JS_EVENT_T EventType() const { return m_eEventType; }
-  bool IsValid() const { return m_bValid; }
-  bool IsUserGesture() const;
-  WideString& Change();
-  WideString ChangeEx() const { return m_WideStrChangeEx; }
-  WideString SourceName() const { return m_strSourceName; }
-  WideString TargetName() const { return m_strTargetName; }
-  int CommitKey() const { return m_nCommitKey; }
-  bool FieldFull() const { return m_bFieldFull; }
-  bool KeyDown() const { return m_bKeyDown; }
-  bool Modifier() const { return m_bModifier; }
-  ByteStringView Name() const;
-  ByteStringView Type() const;
-  bool& Rc();
-  int SelEnd() const;
-  int SelStart() const;
-  void SetSelEnd(int value);
-  void SetSelStart(int value);
-  bool Shift() const { return m_bShift; }
-  bool HasValue() const { return !!m_pValue; }
-  WideString& Value() { return *m_pValue; }
-  bool WillCommit() const { return m_bWillCommit; }
-  CPDFSDK_FormFillEnvironment* GetFormFillEnvironment() const {
-    return m_pTargetFormFillEnv.Get();
-  }
-
-  void SetValueForTest(WideString* pStr) { m_pValue = pStr; }
-  void SetRCForTest(bool* pRC) { m_pbRc = pRC; }
-  void SetStrChangeForTest(WideString* pStrChange) {
-    m_pWideStrChange = pStrChange;
-  }
-  void ResetWillCommitForTest() { m_bWillCommit = false; }
-
- private:
-  void Initialize(JS_EVENT_T type);
-
-  JS_EVENT_T m_eEventType = JET_UNKNOWN;
-  bool m_bValid = false;
-  UnownedPtr<WideString> m_pValue;
-  WideString m_strSourceName;
-  WideString m_strTargetName;
-  WideString m_WideStrChangeDu;
-  WideString m_WideStrChangeEx;
-  UnownedPtr<WideString> m_pWideStrChange;
-  int m_nCommitKey = -1;
-  bool m_bKeyDown = false;
-  bool m_bModifier = false;
-  bool m_bShift = false;
-  int m_nSelEndDu = 0;
-  int m_nSelStartDu = 0;
-  UnownedPtr<int> m_pISelEnd;
-  UnownedPtr<int> m_pISelStart;
-  bool m_bWillCommit = false;
-  bool m_bFieldFull = false;
-  bool m_bRcDu = false;
-  UnownedPtr<bool> m_pbRc;
-  UnownedPtr<CPDF_Bookmark> m_pTargetBookMark;
-  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pTargetFormFillEnv;
-  ObservedPtr<CPDFSDK_Annot> m_pTargetAnnot;
-};
-
-#endif  // FXJS_CJS_EVENTRECORDER_H_
diff --git a/fxjs/cjs_field.cpp b/fxjs/cjs_field.cpp
index b6aa9bb..53b3449 100644
--- a/fxjs/cjs_field.cpp
+++ b/fxjs/cjs_field.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,14 @@
 #include <memory>
 #include <utility>
 
+#include "constants/access_permissions.h"
 #include "constants/annotation_flags.h"
 #include "constants/form_flags.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fpdfsdk/cpdfsdk_pageview.h"
 #include "fpdfsdk/cpdfsdk_widget.h"
@@ -23,11 +25,22 @@
 #include "fxjs/cjs_delaydata.h"
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_icon.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
+#include "v8/include/v8-container.h"
 
 namespace {
 
+constexpr wchar_t kCheckSelector = L'4';
+constexpr wchar_t kCircleSelector = L'l';
+constexpr wchar_t kCrossSelector = L'8';
+constexpr wchar_t kDiamondSelector = L'u';
+constexpr wchar_t kSquareSelector = L'n';
+constexpr wchar_t kStarSelector = L'H';
+
 bool IsCheckBoxOrRadioButton(const CPDF_FormField* pFormField) {
   return pFormField->GetFieldType() == FormFieldType::kCheckBox ||
          pFormField->GetFieldType() == FormFieldType::kRadioButton;
@@ -45,121 +58,99 @@
 
 void UpdateFormField(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                      CPDF_FormField* pFormField,
-                     bool bChangeMark,
-                     bool bResetAP,
-                     bool bRefresh) {
+                     bool bResetAP) {
   CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
-
   if (bResetAP) {
-    std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+    std::vector<ObservedPtr<CPDFSDK_Widget>> widgets;
     pForm->GetWidgets(pFormField, &widgets);
 
     if (IsComboBoxOrTextField(pFormField)) {
-      for (auto& pObserved : widgets) {
-        if (pObserved) {
-          Optional<WideString> sValue =
-              ToCPDFSDKWidget(pObserved.Get())->OnFormat();
-          if (pObserved) {  // Not redundant, may be clobbered by OnFormat.
-            ToCPDFSDKWidget(pObserved.Get())->ResetAppearance(sValue, false);
+      for (auto& pWidget : widgets) {
+        if (pWidget) {
+          absl::optional<WideString> sValue = pWidget->OnFormat();
+          if (pWidget) {  // Not redundant, may be clobbered by OnFormat.
+            pWidget->ResetAppearance(sValue, CPDFSDK_Widget::kValueUnchanged);
           }
         }
       }
     } else {
-      for (auto& pObserved : widgets) {
-        if (pObserved)
-          ToCPDFSDKWidget(pObserved.Get())->ResetAppearance({}, false);
+      for (auto& pWidget : widgets) {
+        if (pWidget) {
+          pWidget->ResetAppearance(absl::nullopt,
+                                   CPDFSDK_Widget::kValueUnchanged);
+        }
       }
     }
   }
 
-  if (bRefresh) {
-    // Refresh the widget list. The calls in |bResetAP| may have caused widgets
-    // to be removed from the list. We need to call |GetWidgets| again to be
-    // sure none of the widgets have been deleted.
-    std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
-    pForm->GetWidgets(pFormField, &widgets);
-
-    // TODO(dsinclair): Determine if all widgets share the same
-    // CPDFSDK_InteractiveForm. If that's the case, we can move the code to
-    // |GetFormFillEnv| out of the loop.
-    for (auto& pObserved : widgets) {
-      if (pObserved) {
-        CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pObserved.Get());
-        pWidget->GetInteractiveForm()->GetFormFillEnv()->UpdateAllViews(
-            nullptr, pWidget);
-      }
-    }
+  // Refresh the widget list. The calls in |bResetAP| may have caused widgets
+  // to be removed from the list. We need to call |GetWidgets| again to be
+  // sure none of the widgets have been deleted.
+  std::vector<ObservedPtr<CPDFSDK_Widget>> widgets;
+  pForm->GetWidgets(pFormField, &widgets);
+  for (auto& pWidget : widgets) {
+    if (pWidget)
+      pFormFillEnv->UpdateAllViews(pWidget.Get());
   }
-
-  if (bChangeMark)
-    pFormFillEnv->SetChangeMark();
+  pFormFillEnv->SetChangeMark();
 }
 
 void UpdateFormControl(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                        CPDF_FormControl* pFormControl,
-                       bool bChangeMark,
-                       bool bResetAP,
-                       bool bRefresh) {
-  ASSERT(pFormControl);
-
+                       bool bResetAP) {
+  DCHECK(pFormControl);
   CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm();
   CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
-
   if (pWidget) {
     ObservedPtr<CPDFSDK_Widget> observed_widget(pWidget);
     if (bResetAP) {
       FormFieldType fieldType = pWidget->GetFieldType();
       if (fieldType == FormFieldType::kComboBox ||
           fieldType == FormFieldType::kTextField) {
-        Optional<WideString> sValue = pWidget->OnFormat();
+        absl::optional<WideString> sValue = pWidget->OnFormat();
         if (!observed_widget)
           return;
-        pWidget->ResetAppearance(sValue, false);
+        pWidget->ResetAppearance(sValue, CPDFSDK_Widget::kValueUnchanged);
       } else {
-        pWidget->ResetAppearance({}, false);
+        pWidget->ResetAppearance(absl::nullopt,
+                                 CPDFSDK_Widget::kValueUnchanged);
       }
       if (!observed_widget)
         return;
     }
-
-    if (bRefresh) {
-      CPDFSDK_InteractiveForm* pWidgetForm = pWidget->GetInteractiveForm();
-      pWidgetForm->GetFormFillEnv()->UpdateAllViews(nullptr, pWidget);
-    }
+    pFormFillEnv->UpdateAllViews(pWidget);
   }
-
-  if (bChangeMark)
-    pFormFillEnv->SetChangeMark();
+  pFormFillEnv->SetChangeMark();
 }
 
-// note: iControlNo = -1, means not a widget.
-void ParseFieldName(const WideString& strFieldNameParsed,
-                    WideString& strFieldName,
-                    int& iControlNo) {
-  auto reverse_it = strFieldNameParsed.rbegin();
-  while (reverse_it != strFieldNameParsed.rend()) {
+struct FieldNameData {
+  FieldNameData(WideString field_name_in, int control_index_in)
+      : field_name(field_name_in), control_index(control_index_in) {}
+
+  WideString field_name;
+  int control_index;
+};
+
+absl::optional<FieldNameData> ParseFieldName(const WideString& field_name) {
+  auto reverse_it = field_name.rbegin();
+  while (reverse_it != field_name.rend()) {
     if (*reverse_it == L'.')
       break;
     ++reverse_it;
   }
-  if (reverse_it == strFieldNameParsed.rend()) {
-    strFieldName = strFieldNameParsed;
-    iControlNo = -1;
-    return;
+  if (reverse_it == field_name.rend()) {
+    return absl::nullopt;
   }
-  WideString suffixal =
-      strFieldNameParsed.Last(reverse_it - strFieldNameParsed.rbegin());
-  iControlNo = FXSYS_wtoi(suffixal.c_str());
-  if (iControlNo == 0) {
+  WideString suffixal = field_name.Last(reverse_it - field_name.rbegin());
+  int control_index = FXSYS_wtoi(suffixal.c_str());
+  if (control_index == 0) {
     suffixal.TrimRight(L' ');
     if (suffixal != L"0") {
-      strFieldName = strFieldNameParsed;
-      iControlNo = -1;
-      return;
+      return absl::nullopt;
     }
   }
-  strFieldName =
-      strFieldNameParsed.First(strFieldNameParsed.rend() - reverse_it - 1);
+  return FieldNameData(field_name.First(field_name.rend() - reverse_it - 1),
+                       control_index);
 }
 
 std::vector<CPDF_FormField*> GetFormFieldsForName(
@@ -168,13 +159,37 @@
   std::vector<CPDF_FormField*> fields;
   CPDFSDK_InteractiveForm* pReaderForm = pFormFillEnv->GetInteractiveForm();
   CPDF_InteractiveForm* pForm = pReaderForm->GetInteractiveForm();
-  for (int i = 0, sz = pForm->CountFields(csFieldName); i < sz; ++i) {
-    if (CPDF_FormField* pFormField = pForm->GetField(i, csFieldName))
+  const size_t sz = pForm->CountFields(csFieldName);
+  for (size_t i = 0; i < sz; ++i) {
+    CPDF_FormField* pFormField = pForm->GetField(i, csFieldName);
+    if (pFormField)
       fields.push_back(pFormField);
   }
   return fields;
 }
 
+CFX_Color GetFormControlColor(CPDF_FormControl* pFormControl,
+                              const ByteString& entry) {
+  switch (pFormControl->GetColorARGB(entry).color_type) {
+    case CFX_Color::Type::kTransparent:
+      return CFX_Color(CFX_Color::Type::kTransparent);
+    case CFX_Color::Type::kGray:
+      return CFX_Color(CFX_Color::Type::kGray,
+                       pFormControl->GetOriginalColorComponent(0, entry));
+    case CFX_Color::Type::kRGB:
+      return CFX_Color(CFX_Color::Type::kRGB,
+                       pFormControl->GetOriginalColorComponent(0, entry),
+                       pFormControl->GetOriginalColorComponent(1, entry),
+                       pFormControl->GetOriginalColorComponent(2, entry));
+    case CFX_Color::Type::kCMYK:
+      return CFX_Color(CFX_Color::Type::kCMYK,
+                       pFormControl->GetOriginalColorComponent(0, entry),
+                       pFormControl->GetOriginalColorComponent(1, entry),
+                       pFormControl->GetOriginalColorComponent(2, entry),
+                       pFormControl->GetOriginalColorComponent(3, entry));
+  }
+}
+
 bool SetWidgetDisplayStatus(CPDFSDK_Widget* pWidget, int value) {
   if (!pWidget)
     return false;
@@ -217,20 +232,20 @@
 void SetBorderStyle(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                     const WideString& swFieldName,
                     int nControlIndex,
-                    const ByteString& string) {
-  ASSERT(pFormFillEnv);
+                    const ByteString& bsString) {
+  DCHECK(pFormFillEnv);
 
-  BorderStyle nBorderStyle = BorderStyle::SOLID;
-  if (string == "solid")
-    nBorderStyle = BorderStyle::SOLID;
-  else if (string == "beveled")
-    nBorderStyle = BorderStyle::BEVELED;
-  else if (string == "dashed")
-    nBorderStyle = BorderStyle::DASH;
-  else if (string == "inset")
-    nBorderStyle = BorderStyle::INSET;
-  else if (string == "underline")
-    nBorderStyle = BorderStyle::UNDERLINE;
+  BorderStyle nBorderStyle = BorderStyle::kSolid;
+  if (bsString == "solid")
+    nBorderStyle = BorderStyle::kSolid;
+  else if (bsString == "beveled")
+    nBorderStyle = BorderStyle::kBeveled;
+  else if (bsString == "dashed")
+    nBorderStyle = BorderStyle::kDash;
+  else if (bsString == "inset")
+    nBorderStyle = BorderStyle::kInset;
+  else if (bsString == "underline")
+    nBorderStyle = BorderStyle::kUnderline;
   else
     return;
 
@@ -250,7 +265,7 @@
         }
       }
       if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+        UpdateFormField(pFormFillEnv, pFormField, true);
     } else {
       if (nControlIndex >= pFormField->CountControls())
         return;
@@ -260,7 +275,7 @@
         if (pWidget) {
           if (pWidget->GetBorderStyle() != nBorderStyle) {
             pWidget->SetBorderStyle(nBorderStyle);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+            UpdateFormControl(pFormFillEnv, pFormControl, true);
           }
         }
       }
@@ -272,7 +287,7 @@
                             const WideString& swFieldName,
                             int nControlIndex,
                             const std::vector<uint32_t>& array) {
-  ASSERT(pFormFillEnv);
+  DCHECK(pFormFillEnv);
   std::vector<CPDF_FormField*> FieldArray =
       GetFormFieldsForName(pFormFillEnv, swFieldName);
 
@@ -287,11 +302,11 @@
         break;
       if (array[i] < static_cast<uint32_t>(pFormField->CountOptions()) &&
           !pFormField->IsItemSelected(array[i])) {
-        pFormField->SetItemSelection(array[i], true,
+        pFormField->SetItemSelection(array[i],
                                      NotificationOption::kDoNotNotify);
       }
     }
-    UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+    UpdateFormField(pFormFillEnv, pFormField, true);
   }
 }
 
@@ -307,7 +322,7 @@
       bool bAnySet = false;
       for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
         CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
+        DCHECK(pFormControl);
 
         CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
         if (SetWidgetDisplayStatus(pWidget, number))
@@ -315,7 +330,7 @@
       }
 
       if (bAnySet)
-        UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+        UpdateFormField(pFormFillEnv, pFormField, false);
     } else {
       if (nControlIndex >= pFormField->CountControls())
         return;
@@ -326,7 +341,7 @@
 
       CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
       if (SetWidgetDisplayStatus(pWidget, number))
-        UpdateFormControl(pFormFillEnv, pFormControl, true, false, true);
+        UpdateFormControl(pFormFillEnv, pFormControl, false);
     }
   }
 }
@@ -351,7 +366,7 @@
       bool bSet = false;
       for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
         CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
+        DCHECK(pFormControl);
 
         if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
           if (number != pWidget->GetBorderWidth()) {
@@ -361,7 +376,7 @@
         }
       }
       if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+        UpdateFormField(pFormFillEnv, pFormField, true);
     } else {
       if (nControlIndex >= pFormField->CountControls())
         return;
@@ -370,7 +385,7 @@
         if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
           if (number != pWidget->GetBorderWidth()) {
             pWidget->SetBorderWidth(number);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+            UpdateFormControl(pFormFillEnv, pFormControl, true);
           }
         }
       }
@@ -390,7 +405,7 @@
       bool bSet = false;
       for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) {
         CPDF_FormControl* pFormControl = pFormField->GetControl(i);
-        ASSERT(pFormControl);
+        DCHECK(pFormControl);
 
         if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) {
           CFX_FloatRect crRect = rect;
@@ -410,7 +425,7 @@
       }
 
       if (bSet)
-        UpdateFormField(pFormFillEnv, pFormField, true, true, true);
+        UpdateFormField(pFormFillEnv, pFormField, true);
 
       continue;
     }
@@ -428,7 +443,7 @@
           if (crRect.left != rcOld.left || crRect.right != rcOld.right ||
               crRect.top != rcOld.top || crRect.bottom != rcOld.bottom) {
             pWidget->SetRect(crRect);
-            UpdateFormControl(pFormFillEnv, pFormControl, true, true, true);
+            UpdateFormControl(pFormFillEnv, pFormControl, true);
           }
         }
       }
@@ -440,7 +455,7 @@
                    const WideString& swFieldName,
                    int nControlIndex,
                    const std::vector<WideString>& strArray) {
-  ASSERT(pFormFillEnv);
+  DCHECK(pFormFillEnv);
   if (strArray.empty())
     return;
 
@@ -448,7 +463,7 @@
       GetFormFieldsForName(pFormFillEnv, swFieldName);
 
   for (CPDF_FormField* pFormField : FieldArray) {
-    if (pFormField->GetFullName().Compare(swFieldName) != 0)
+    if (pFormField->GetFullName() != swFieldName)
       continue;
 
     switch (pFormField->GetFieldType()) {
@@ -456,14 +471,14 @@
       case FormFieldType::kComboBox:
         if (pFormField->GetValue() != strArray[0]) {
           pFormField->SetValue(strArray[0], NotificationOption::kNotify);
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+          UpdateFormField(pFormFillEnv, pFormField, false);
         }
         break;
       case FormFieldType::kCheckBox:
       case FormFieldType::kRadioButton:
         if (pFormField->GetValue() != strArray[0]) {
           pFormField->SetValue(strArray[0], NotificationOption::kNotify);
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+          UpdateFormField(pFormFillEnv, pFormField, false);
         }
         break;
       case FormFieldType::kListBox: {
@@ -479,10 +494,9 @@
           for (const auto& str : strArray) {
             int index = pFormField->FindOption(str);
             if (!pFormField->IsItemSelected(index))
-              pFormField->SetItemSelection(index, true,
-                                           NotificationOption::kNotify);
+              pFormField->SetItemSelection(index, NotificationOption::kNotify);
           }
-          UpdateFormField(pFormFillEnv, pFormField, true, false, true);
+          UpdateFormField(pFormFillEnv, pFormField, false);
         }
         break;
       }
@@ -492,6 +506,19 @@
   }
 }
 
+wchar_t GetSelectorFromCaptionForFieldType(const WideString& caption,
+                                           CPDF_FormField::Type type) {
+  if (!caption.IsEmpty())
+    return caption[0];
+
+  switch (type) {
+    case CPDF_FormField::kRadioButton:
+      return kCircleSelector;
+    default:
+      return kCheckSelector;
+  }
+}
+
 }  // namespace
 
 const JSPropertySpec CJS_Field::PropertySpecs[] = {
@@ -585,11 +612,11 @@
     {"signatureSign", signatureSign_static},
     {"signatureValidate", signatureValidate_static}};
 
-int CJS_Field::ObjDefnID = -1;
+uint32_t CJS_Field::ObjDefnID = 0;
 const char CJS_Field::kName[] = "Field";
 
 // static
-int CJS_Field::GetObjDefnID() {
+uint32_t CJS_Field::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -610,9 +637,10 @@
                             const WideString& csFieldName) {
   m_pJSDoc.Reset(pDocument);
   m_pFormFillEnv.Reset(pDocument->GetFormFillEnv());
-  m_bCanSet = m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM) ||
-              m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) ||
-              m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY);
+  m_bCanSet = m_pFormFillEnv->HasPermissions(
+      pdfium::access_permissions::kFillForm |
+      pdfium::access_permissions::kModifyAnnotation |
+      pdfium::access_permissions::kModifyContent);
 
   CPDFSDK_InteractiveForm* pRDForm = m_pFormFillEnv->GetInteractiveForm();
   CPDF_InteractiveForm* pForm = pRDForm->GetInteractiveForm();
@@ -620,14 +648,12 @@
   swFieldNameTemp.Replace(L"..", L".");
 
   if (pForm->CountFields(swFieldNameTemp) <= 0) {
-    WideString strFieldName;
-    int iControlNo = -1;
-    ParseFieldName(swFieldNameTemp, strFieldName, iControlNo);
-    if (iControlNo == -1)
+    absl::optional<FieldNameData> parsed_data = ParseFieldName(swFieldNameTemp);
+    if (!parsed_data.has_value())
       return false;
 
-    m_FieldName = strFieldName;
-    m_nFormControlIndex = iControlNo;
+    m_FieldName = parsed_data.value().field_name;
+    m_nFormControlIndex = parsed_data.value().control_index;
     return true;
   }
 
@@ -656,7 +682,7 @@
 }
 
 CJS_Result CJS_Field::get_alignment(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -682,7 +708,7 @@
 
 CJS_Result CJS_Field::set_alignment(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
@@ -690,7 +716,7 @@
 }
 
 CJS_Result CJS_Field::get_border_style(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -702,15 +728,15 @@
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   switch (pWidget->GetBorderStyle()) {
-    case BorderStyle::SOLID:
+    case BorderStyle::kSolid:
       return CJS_Result::Success(pRuntime->NewString("solid"));
-    case BorderStyle::DASH:
+    case BorderStyle::kDash:
       return CJS_Result::Success(pRuntime->NewString("dashed"));
-    case BorderStyle::BEVELED:
+    case BorderStyle::kBeveled:
       return CJS_Result::Success(pRuntime->NewString("beveled"));
-    case BorderStyle::INSET:
+    case BorderStyle::kInset:
       return CJS_Result::Success(pRuntime->NewString("inset"));
-    case BorderStyle::UNDERLINE:
+    case BorderStyle::kUnderline:
       return CJS_Result::Success(pRuntime->NewString("underline"));
   }
   return CJS_Result::Success(pRuntime->NewString(""));
@@ -718,11 +744,11 @@
 
 CJS_Result CJS_Field::set_border_style(CJS_Runtime* pRuntime,
                                        v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
-  ByteString byte_str = pRuntime->ToWideString(vp).ToDefANSI();
+  ByteString byte_str = pRuntime->ToByteString(vp);
   if (m_bDelay) {
     AddDelay_String(FP_BORDERSTYLE, byte_str);
   } else {
@@ -733,7 +759,7 @@
 }
 
 CJS_Result CJS_Field::get_button_align_x(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -753,14 +779,14 @@
 
 CJS_Result CJS_Field::set_button_align_x(CJS_Runtime* pRuntime,
                                          v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_align_y(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -780,14 +806,14 @@
 
 CJS_Result CJS_Field::set_button_align_y(CJS_Runtime* pRuntime,
                                          v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_fit_bounds(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -806,14 +832,14 @@
 
 CJS_Result CJS_Field::set_button_fit_bounds(CJS_Runtime* pRuntime,
                                             v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_position(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -832,14 +858,14 @@
 
 CJS_Result CJS_Field::set_button_position(CJS_Runtime* pRuntime,
                                           v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_scale_how(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -852,20 +878,20 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  return CJS_Result::Success(pRuntime->NewBoolean(
-      pFormControl->GetIconFit().IsProportionalScale() ? 0 : 1));
+  return CJS_Result::Success(
+      pRuntime->NewBoolean(!pFormControl->GetIconFit().IsProportionalScale()));
 }
 
 CJS_Result CJS_Field::set_button_scale_how(CJS_Runtime* pRuntime,
                                            v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_button_scale_when(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -879,34 +905,27 @@
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   CPDF_IconFit IconFit = pFormControl->GetIconFit();
-  int ScaleM = IconFit.GetScaleMethod();
-  switch (ScaleM) {
-    case CPDF_IconFit::Always:
+  CPDF_IconFit::ScaleMethod scale_method = IconFit.GetScaleMethod();
+  switch (scale_method) {
+    case CPDF_IconFit::ScaleMethod::kAlways:
+    case CPDF_IconFit::ScaleMethod::kBigger:
+    case CPDF_IconFit::ScaleMethod::kNever:
+    case CPDF_IconFit::ScaleMethod::kSmaller:
       return CJS_Result::Success(
-          pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Always)));
-    case CPDF_IconFit::Bigger:
-      return CJS_Result::Success(
-          pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Bigger)));
-    case CPDF_IconFit::Never:
-      return CJS_Result::Success(
-          pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Never)));
-    case CPDF_IconFit::Smaller:
-      return CJS_Result::Success(
-          pRuntime->NewNumber(static_cast<int32_t>(CPDF_IconFit::Smaller)));
+          pRuntime->NewNumber(static_cast<int>(scale_method)));
   }
-  return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::set_button_scale_when(CJS_Runtime* pRuntime,
                                             v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_calc_order_index(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -917,20 +936,20 @@
 
   CPDFSDK_InteractiveForm* pRDForm = m_pFormFillEnv->GetInteractiveForm();
   CPDF_InteractiveForm* pForm = pRDForm->GetInteractiveForm();
-  return CJS_Result::Success(pRuntime->NewNumber(
-      static_cast<int32_t>(pForm->FindFieldInCalculationOrder(pFormField))));
+  return CJS_Result::Success(
+      pRuntime->NewNumber(pForm->FindFieldInCalculationOrder(pFormField)));
 }
 
 CJS_Result CJS_Field::set_calc_order_index(CJS_Runtime* pRuntime,
                                            v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_char_limit(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -944,14 +963,14 @@
 
 CJS_Result CJS_Field::set_char_limit(CJS_Runtime* pRuntime,
                                      v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_comb(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -965,14 +984,14 @@
 }
 
 CJS_Result CJS_Field::set_comb(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_commit_on_sel_change(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -988,7 +1007,7 @@
 
 CJS_Result CJS_Field::set_commit_on_sel_change(CJS_Runtime* pRuntime,
                                                v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1027,7 +1046,7 @@
   std::vector<uint32_t> array;
   if (vp->IsNumber()) {
     array.push_back(pRuntime->ToInt32(vp));
-  } else if (!vp.IsEmpty() && vp->IsArray()) {
+  } else if (fxv8::IsArray(vp)) {
     v8::Local<v8::Array> SelArray = pRuntime->ToArray(vp);
     for (size_t i = 0; i < pRuntime->GetArrayLength(SelArray); i++) {
       array.push_back(
@@ -1054,7 +1073,7 @@
 }
 
 CJS_Result CJS_Field::get_default_value(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1071,14 +1090,14 @@
 
 CJS_Result CJS_Field::set_default_value(CJS_Runtime* pRuntime,
                                         v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_do_not_scroll(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1093,14 +1112,14 @@
 
 CJS_Result CJS_Field::set_do_not_scroll(CJS_Runtime* pRuntime,
                                         v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_do_not_spell_check(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1116,21 +1135,12 @@
 
 CJS_Result CJS_Field::set_do_not_spell_check(CJS_Runtime* pRuntime,
                                              v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
-void CJS_Field::SetDelay(bool bDelay) {
-  m_bDelay = bDelay;
-
-  if (m_bDelay)
-    return;
-  if (m_pJSDoc)
-    m_pJSDoc->DoFieldDelay(m_FieldName, m_nFormControlIndex);
-}
-
 CJS_Result CJS_Field::get_delay(CJS_Runtime* pRuntime) {
   return CJS_Result::Success(pRuntime->NewBoolean(m_bDelay));
 }
@@ -1253,7 +1263,7 @@
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   return CJS_Result::Success();
@@ -1295,30 +1305,7 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  int iColorType;
-  pFormControl->GetBackgroundColor(iColorType);
-
-  CFX_Color color;
-  if (iColorType == CFX_Color::kTransparent) {
-    color = CFX_Color(CFX_Color::kTransparent);
-  } else if (iColorType == CFX_Color::kGray) {
-    color = CFX_Color(CFX_Color::kGray,
-                      pFormControl->GetOriginalBackgroundColor(0));
-  } else if (iColorType == CFX_Color::kRGB) {
-    color =
-        CFX_Color(CFX_Color::kRGB, pFormControl->GetOriginalBackgroundColor(0),
-                  pFormControl->GetOriginalBackgroundColor(1),
-                  pFormControl->GetOriginalBackgroundColor(2));
-  } else if (iColorType == CFX_Color::kCMYK) {
-    color =
-        CFX_Color(CFX_Color::kCMYK, pFormControl->GetOriginalBackgroundColor(0),
-                  pFormControl->GetOriginalBackgroundColor(1),
-                  pFormControl->GetOriginalBackgroundColor(2),
-                  pFormControl->GetOriginalBackgroundColor(3));
-  } else {
-    return CJS_Result::Failure(JSMessage::kValueError);
-  }
-
+  CFX_Color color = GetFormControlColor(pFormControl, pdfium::appearance::kBG);
   v8::Local<v8::Value> array =
       CJS_Color::ConvertPWLColorToArray(pRuntime, color);
   if (array.IsEmpty())
@@ -1333,7 +1320,7 @@
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   return CJS_Result::Success();
 }
@@ -1369,7 +1356,7 @@
 }
 
 CJS_Result CJS_Field::get_highlight(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1384,15 +1371,15 @@
 
   int eHM = pFormControl->GetHighlightingMode();
   switch (eHM) {
-    case CPDF_FormControl::None:
+    case CPDF_FormControl::kNone:
       return CJS_Result::Success(pRuntime->NewString("none"));
-    case CPDF_FormControl::Push:
+    case CPDF_FormControl::kPush:
       return CJS_Result::Success(pRuntime->NewString("push"));
-    case CPDF_FormControl::Invert:
+    case CPDF_FormControl::kInvert:
       return CJS_Result::Success(pRuntime->NewString("invert"));
-    case CPDF_FormControl::Outline:
+    case CPDF_FormControl::kOutline:
       return CJS_Result::Success(pRuntime->NewString("outline"));
-    case CPDF_FormControl::Toggle:
+    case CPDF_FormControl::kToggle:
       return CJS_Result::Success(pRuntime->NewString("toggle"));
   }
   return CJS_Result::Success();
@@ -1400,7 +1387,7 @@
 
 CJS_Result CJS_Field::set_highlight(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1441,7 +1428,7 @@
 }
 
 CJS_Result CJS_Field::get_multiline(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1456,14 +1443,14 @@
 
 CJS_Result CJS_Field::set_multiline(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_multiple_selection(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
@@ -1478,7 +1465,7 @@
 
 CJS_Result CJS_Field::set_multiple_selection(CJS_Runtime* pRuntime,
                                              v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1517,25 +1504,20 @@
   if (!pFormField)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  std::vector<ObservedPtr<CPDFSDK_Annot>> widgets;
+  std::vector<ObservedPtr<CPDFSDK_Widget>> widgets;
   m_pFormFillEnv->GetInteractiveForm()->GetWidgets(pFormField, &widgets);
   if (widgets.empty())
     return CJS_Result::Success(pRuntime->NewNumber(-1));
 
   v8::Local<v8::Array> PageArray = pRuntime->NewArray();
   int i = 0;
-  for (const auto& pObserved : widgets) {
-    if (!pObserved)
-      return CJS_Result::Failure(JSMessage::kBadObjectError);
-
-    auto* pWidget = ToCPDFSDKWidget(pObserved.Get());
-    CPDFSDK_PageView* pPageView = pWidget->GetPageView();
-    if (!pPageView)
+  for (const auto& pWidget : widgets) {
+    if (!pWidget)
       return CJS_Result::Failure(JSMessage::kBadObjectError);
 
     pRuntime->PutArrayElement(
         PageArray, i,
-        pRuntime->NewNumber(static_cast<int32_t>(pPageView->GetPageIndex())));
+        pRuntime->NewNumber(pWidget->GetPageView()->GetPageIndex()));
     ++i;
   }
   return CJS_Result::Success(PageArray);
@@ -1546,7 +1528,7 @@
 }
 
 CJS_Result CJS_Field::get_password(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1561,7 +1543,7 @@
 
 CJS_Result CJS_Field::set_password(CJS_Runtime* pRuntime,
                                    v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1611,7 +1593,7 @@
       }
 
       if (bSet)
-        UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, false, true);
+        UpdateFormField(m_pFormFillEnv.Get(), pFormField, false);
 
       continue;
     }
@@ -1631,8 +1613,7 @@
         if (dwFlags != pWidget->GetFlags()) {
           pWidget->SetFlags(dwFlags);
           UpdateFormControl(m_pFormFillEnv.Get(),
-                            pFormField->GetControl(m_nFormControlIndex), true,
-                            false, true);
+                            pFormField->GetControl(m_nFormControlIndex), false);
         }
       }
     }
@@ -1674,11 +1655,21 @@
 
 CJS_Result CJS_Field::set_readonly(CJS_Runtime* pRuntime,
                                    v8::Local<v8::Value> vp) {
-  std::vector<CPDF_FormField*> FieldArray = GetFormFields();
-  if (FieldArray.empty())
+  CPDF_FormField* pFormField = GetFirstFormField();
+  if (!pFormField)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
+
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
+
+  const bool bReadOnly = pRuntime->ToBoolean(vp);
+  const uint32_t dwFlags = pFormField->GetFieldFlags();
+  const uint32_t dwNewFlags = bReadOnly
+                                  ? (dwFlags | pdfium::form_flags::kReadOnly)
+                                  : (dwFlags & ~pdfium::form_flags::kReadOnly);
+  if (dwNewFlags != dwFlags)
+    pFormField->SetFieldFlags(dwNewFlags);
+
   return CJS_Result::Success();
 }
 
@@ -1709,24 +1700,23 @@
 CJS_Result CJS_Field::set_rect(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kValueError);
 
   v8::Local<v8::Array> rcArray = pRuntime->ToArray(vp);
   if (pRuntime->GetArrayLength(rcArray) < 4)
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  float pArray[4];
-  pArray[0] = static_cast<float>(
+  float f0 = static_cast<float>(
       pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 0)));
-  pArray[1] = static_cast<float>(
+  float f1 = static_cast<float>(
       pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 1)));
-  pArray[2] = static_cast<float>(
+  float f2 = static_cast<float>(
       pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 2)));
-  pArray[3] = static_cast<float>(
+  float f3 = static_cast<float>(
       pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 3)));
 
-  CFX_FloatRect crRect(pArray);
+  CFX_FloatRect crRect(f0, f1, f2, f3);
   if (m_bDelay) {
     AddDelay_Rect(FP_RECT, crRect);
   } else {
@@ -1758,7 +1748,7 @@
 }
 
 CJS_Result CJS_Field::get_rich_text(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1773,7 +1763,7 @@
 
 CJS_Result CJS_Field::set_rich_text(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1789,7 +1779,7 @@
 }
 
 CJS_Result CJS_Field::get_rotation(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1804,7 +1794,7 @@
 
 CJS_Result CJS_Field::set_rotation(CJS_Runtime* pRuntime,
                                    v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1828,28 +1818,7 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  int iColorType;
-  pFormControl->GetBorderColor(iColorType);
-
-  CFX_Color color;
-  if (iColorType == CFX_Color::kTransparent) {
-    color = CFX_Color(CFX_Color::kTransparent);
-  } else if (iColorType == CFX_Color::kGray) {
-    color =
-        CFX_Color(CFX_Color::kGray, pFormControl->GetOriginalBorderColor(0));
-  } else if (iColorType == CFX_Color::kRGB) {
-    color = CFX_Color(CFX_Color::kRGB, pFormControl->GetOriginalBorderColor(0),
-                      pFormControl->GetOriginalBorderColor(1),
-                      pFormControl->GetOriginalBorderColor(2));
-  } else if (iColorType == CFX_Color::kCMYK) {
-    color = CFX_Color(CFX_Color::kCMYK, pFormControl->GetOriginalBorderColor(0),
-                      pFormControl->GetOriginalBorderColor(1),
-                      pFormControl->GetOriginalBorderColor(2),
-                      pFormControl->GetOriginalBorderColor(3));
-  } else {
-    return CJS_Result::Failure(JSMessage::kObjectTypeError);
-  }
-
+  CFX_Color color = GetFormControlColor(pFormControl, pdfium::appearance::kBC);
   v8::Local<v8::Value> array =
       CJS_Color::ConvertPWLColorToArray(pRuntime, color);
   if (array.IsEmpty())
@@ -1861,13 +1830,13 @@
                                        v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_style(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1880,37 +1849,37 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  WideString csWCaption = pFormControl->GetNormalCaption();
-  wchar_t selector = !csWCaption.IsEmpty() ? csWCaption[0] : L'4';
+  wchar_t selector = GetSelectorFromCaptionForFieldType(
+      pFormControl->GetNormalCaption(), pFormControl->GetType());
 
   ByteString csBCaption;
   switch (selector) {
-    case L'l':
+    case kCircleSelector:
       csBCaption = "circle";
       break;
-    case L'8':
+    case kCrossSelector:
       csBCaption = "cross";
       break;
-    case L'u':
+    case kDiamondSelector:
       csBCaption = "diamond";
       break;
-    case L'n':
+    case kSquareSelector:
       csBCaption = "square";
       break;
-    case L'H':
+    case kStarSelector:
       csBCaption = "star";
       break;
-    default:  // L'4'
+    case kCheckSelector:
+    default:
       csBCaption = "check";
       break;
   }
-  return CJS_Result::Success(pRuntime->NewString(
-      WideString::FromDefANSI(csBCaption.AsStringView()).AsStringView()));
+  return CJS_Result::Success(pRuntime->NewString(csBCaption.AsStringView()));
 }
 
 CJS_Result CJS_Field::set_style(CJS_Runtime* pRuntime,
                                 v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -1934,21 +1903,21 @@
   if (!pFormControl)
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  Optional<CFX_Color::Type> iColorType;
-  FX_ARGB color;
   CPDF_DefaultAppearance FieldAppearance = pFormControl->GetDefaultAppearance();
-  std::tie(iColorType, color) = FieldAppearance.GetColor();
+  absl::optional<CFX_Color::TypeAndARGB> maybe_type_argb_pair =
+      FieldAppearance.GetColorARGB();
 
   CFX_Color crRet;
-  if (!iColorType || *iColorType == CFX_Color::kTransparent) {
-    crRet = CFX_Color(CFX_Color::kTransparent);
-  } else {
+  if (maybe_type_argb_pair.has_value() &&
+      maybe_type_argb_pair.value().color_type !=
+          CFX_Color::Type::kTransparent) {
     int32_t a;
     int32_t r;
     int32_t g;
     int32_t b;
-    std::tie(a, r, g, b) = ArgbDecode(color);
-    crRet = CFX_Color(CFX_Color::kRGB, r / 255.0f, g / 255.0f, b / 255.0f);
+    std::tie(a, r, g, b) = ArgbDecode(maybe_type_argb_pair.value().argb);
+    crRet =
+        CFX_Color(CFX_Color::Type::kRGB, r / 255.0f, g / 255.0f, b / 255.0f);
   }
 
   v8::Local<v8::Value> array =
@@ -1962,13 +1931,13 @@
                                      v8::Local<v8::Value> vp) {
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (vp.IsEmpty() || !vp->IsArray())
+  if (!fxv8::IsArray(vp))
     return CJS_Result::Failure(JSMessage::kBadObjectError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_text_font(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -1986,7 +1955,8 @@
     return CJS_Result::Failure(JSMessage::kObjectTypeError);
   }
 
-  Optional<WideString> wsFontName = pFormControl->GetDefaultControlFontName();
+  absl::optional<WideString> wsFontName =
+      pFormControl->GetDefaultControlFontName();
   if (!wsFontName.has_value())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1996,17 +1966,17 @@
 
 CJS_Result CJS_Field::set_text_font(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
-  if (pRuntime->ToWideString(vp).ToDefANSI().IsEmpty())
+  if (pRuntime->ToByteString(vp).IsEmpty())
     return CJS_Result::Failure(JSMessage::kValueError);
   return CJS_Result::Success();
 }
 
 CJS_Result CJS_Field::get_text_size(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -2024,7 +1994,7 @@
 
 CJS_Result CJS_Field::set_text_size(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -2062,7 +2032,7 @@
 }
 
 CJS_Result CJS_Field::get_user_name(CJS_Runtime* pRuntime) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
 
   CPDF_FormField* pFormField = GetFirstFormField();
   if (!pFormField)
@@ -2074,7 +2044,7 @@
 
 CJS_Result CJS_Field::set_user_name(CJS_Runtime* pRuntime,
                                     v8::Local<v8::Value> vp) {
-  ASSERT(m_pFormFillEnv);
+  DCHECK(m_pFormFillEnv);
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
   return CJS_Result::Success();
@@ -2102,7 +2072,7 @@
           iIndex = pFormField->GetSelectedIndex(i);
           ElementValue = pRuntime->NewString(
               pFormField->GetOptionValue(iIndex).AsStringView());
-          if (wcslen(pRuntime->ToWideString(ElementValue).c_str()) == 0) {
+          if (pRuntime->ToWideString(ElementValue).IsEmpty()) {
             ElementValue = pRuntime->NewString(
                 pFormField->GetOptionLabel(iIndex).AsStringView());
           }
@@ -2143,7 +2113,7 @@
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
   std::vector<WideString> strArray;
-  if (!vp.IsEmpty() && vp->IsArray()) {
+  if (fxv8::IsArray(vp)) {
     v8::Local<v8::Array> ValueArray = pRuntime->ToArray(vp);
     for (size_t i = 0; i < pRuntime->GetArrayLength(ValueArray); i++) {
       strArray.push_back(
@@ -2214,7 +2184,7 @@
     WideString wsFileName = m_pFormFillEnv->JS_fieldBrowse();
     if (!wsFileName.IsEmpty()) {
       pFormField->SetValue(wsFileName, NotificationOption::kDoNotNotify);
-      UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, true, true);
+      UpdateFormField(m_pFormFillEnv.Get(), pFormField, true);
     }
     return CJS_Result::Success();
   }
@@ -2225,8 +2195,7 @@
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   int nface = 0;
-  int iSize = params.size();
-  if (iSize >= 1)
+  if (params.size() >= 1)
     nface = pRuntime->ToInt32(params[0]);
 
   CPDF_FormField* pFormField = GetFirstFormField();
@@ -2280,7 +2249,8 @@
   if (pObj.IsEmpty())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  auto* pJS_Icon = static_cast<CJS_Icon*>(CFXJS_Engine::GetObjectPrivate(pObj));
+  auto* pJS_Icon = static_cast<CJS_Icon*>(
+      CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
   return pJS_Icon ? CJS_Result::Success(pJS_Icon->ToV8Object())
                   : CJS_Result::Failure(JSMessage::kBadObjectError);
 }
@@ -2306,8 +2276,8 @@
 CJS_Result CJS_Field::checkThisBox(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  int iSize = params.size();
-  if (iSize < 1)
+  const size_t nSize = params.size();
+  if (nSize == 0)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   if (!m_bCanSet)
@@ -2315,7 +2285,7 @@
 
   int nWidget = pRuntime->ToInt32(params[0]);
   bool bCheckit = true;
-  if (iSize >= 2)
+  if (nSize >= 2)
     bCheckit = pRuntime->ToBoolean(params[1]);
 
   CPDF_FormField* pFormField = GetFirstFormField();
@@ -2331,7 +2301,7 @@
   // TODO(weili): Check whether anything special needed for radio button.
   // (When pFormField->GetFieldType() == FormFieldType::kRadioButton.)
   pFormField->CheckControl(nWidget, bCheckit, NotificationOption::kNotify);
-  UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, true, true);
+  UpdateFormField(m_pFormFillEnv.Get(), pFormField, true);
   return CJS_Result::Success();
 }
 
@@ -2347,8 +2317,7 @@
   if (!m_bCanSet)
     return CJS_Result::Failure(JSMessage::kReadOnlyError);
 
-  int iSize = params.size();
-  if (iSize < 1)
+  if (params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CPDF_FormField* pFormField = GetFirstFormField();
@@ -2378,7 +2347,7 @@
 
   std::vector<std::unique_ptr<WideString>> swSort;
   for (CPDF_FormField* pFormField : FieldArray) {
-    swSort.push_back(pdfium::MakeUnique<WideString>(pFormField->GetFullName()));
+    swSort.push_back(std::make_unique<WideString>(pFormField->GetFullName()));
   }
 
   std::sort(swSort.begin(), swSort.end(),
@@ -2393,8 +2362,8 @@
     if (pObj.IsEmpty())
       return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-    auto* pJSField =
-        static_cast<CJS_Field*>(CFXJS_Engine::GetObjectPrivate(pObj));
+    auto* pJSField = static_cast<CJS_Field*>(
+        CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj));
     pJSField->AttachField(m_pJSDoc.Get(), *pStr);
     pRuntime->PutArrayElement(FormFieldArray, j++,
                               pJSField
@@ -2407,13 +2376,13 @@
 CJS_Result CJS_Field::getItemAt(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  int iSize = params.size();
+  const size_t nSize = params.size();
   int nIdx = -1;
-  if (iSize >= 1)
+  if (nSize >= 1)
     nIdx = pRuntime->ToInt32(params[0]);
 
   bool bExport = true;
-  if (iSize >= 2)
+  if (nSize >= 2)
     bExport = pRuntime->ToBoolean(params[1]);
 
   CPDF_FormField* pFormField = GetFirstFormField();
@@ -2509,18 +2478,16 @@
   if (nCount == 1) {
     pWidget = pForm->GetWidget(pFormField->GetControl(0));
   } else {
-    IPDF_Page* pPage = IPDFPageFromFPDFPage(m_pFormFillEnv->GetCurrentPage());
+    IPDF_Page* pPage = m_pFormFillEnv->GetCurrentPage();
     if (!pPage)
       return CJS_Result::Failure(JSMessage::kBadObjectError);
-    if (CPDFSDK_PageView* pCurPageView =
-            m_pFormFillEnv->GetPageView(pPage, true)) {
-      for (int32_t i = 0; i < nCount; i++) {
-        if (CPDFSDK_Widget* pTempWidget =
-                pForm->GetWidget(pFormField->GetControl(i))) {
-          if (pTempWidget->GetPDFPage() == pCurPageView->GetPDFPage()) {
-            pWidget = pTempWidget;
-            break;
-          }
+    CPDFSDK_PageView* pCurPageView = m_pFormFillEnv->GetOrCreatePageView(pPage);
+    for (int32_t i = 0; i < nCount; i++) {
+      if (CPDFSDK_Widget* pTempWidget =
+              pForm->GetWidget(pFormField->GetControl(i))) {
+        if (pTempWidget->GetPDFPage() == pCurPageView->GetPDFPage()) {
+          pWidget = pTempWidget;
+          break;
         }
       }
     }
@@ -2528,7 +2495,7 @@
 
   if (pWidget) {
     ObservedPtr<CPDFSDK_Annot> pObserved(pWidget);
-    m_pFormFillEnv->SetFocusAnnot(&pObserved);
+    m_pFormFillEnv->SetFocusAnnot(pObserved);
   }
 
   return CJS_Result::Success();
@@ -2581,30 +2548,39 @@
   return CJS_Result::Failure(JSMessage::kNotSupportedError);
 }
 
+void CJS_Field::SetDelay(bool bDelay) {
+  m_bDelay = bDelay;
+  if (m_bDelay)
+    return;
+
+  if (m_pJSDoc)
+    m_pJSDoc->DoFieldDelay(m_FieldName, m_nFormControlIndex);
+}
+
 void CJS_Field::AddDelay_Int(FIELD_PROP prop, int32_t n) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->num = n;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
 void CJS_Field::AddDelay_Bool(FIELD_PROP prop, bool b) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->b = b;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
 void CJS_Field::AddDelay_String(FIELD_PROP prop, const ByteString& str) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->bytestring = str;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
 void CJS_Field::AddDelay_Rect(FIELD_PROP prop, const CFX_FloatRect& rect) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->rect = rect;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
@@ -2612,7 +2588,7 @@
 void CJS_Field::AddDelay_WordArray(FIELD_PROP prop,
                                    const std::vector<uint32_t>& array) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->wordarray = array;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
@@ -2620,14 +2596,14 @@
 void CJS_Field::AddDelay_WideStringArray(FIELD_PROP prop,
                                          const std::vector<WideString>& array) {
   auto pNewData =
-      pdfium::MakeUnique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
+      std::make_unique<CJS_DelayData>(prop, m_nFormControlIndex, m_FieldName);
   pNewData->widestringarray = array;
   m_pJSDoc->AddDelayData(std::move(pNewData));
 }
 
 void CJS_Field::DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                         CJS_DelayData* pData) {
-  ASSERT(pFormFillEnv);
+  DCHECK(pFormFillEnv);
   switch (pData->eProp) {
     case FP_BORDERSTYLE:
       SetBorderStyle(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
diff --git a/fxjs/cjs_field.h b/fxjs/cjs_field.h
index d772e66..7a66277 100644
--- a/fxjs/cjs_field.h
+++ b/fxjs/cjs_field.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,8 +13,8 @@
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
 
+class CFX_FloatRect;
 class CPDF_FormControl;
-class CPDFSDK_Widget;
 struct CJS_DelayData;
 
 enum FIELD_PROP {
@@ -29,7 +29,7 @@
 
 class CJS_Field final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
   static void DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv,
                       CJS_DelayData* pData);
@@ -123,7 +123,7 @@
   CJS_Result set_text_color(CJS_Runtime* pRuntime, v8::Local<v8::Value> vp);
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
   static const JSMethodSpec MethodSpecs[];
@@ -349,11 +349,11 @@
   CJS_Result signatureValidate(CJS_Runtime* pRuntime,
                                const std::vector<v8::Local<v8::Value>>& params);
 
-  void SetDelay(bool bDelay);
   std::vector<CPDF_FormField*> GetFormFields() const;
   CPDF_FormField* GetFirstFormField() const;
   CPDF_FormControl* GetSmartFieldControl(CPDF_FormField* pFormField);
 
+  void SetDelay(bool bDelay);
   void AddDelay_Int(FIELD_PROP prop, int32_t n);
   void AddDelay_Bool(FIELD_PROP prop, bool b);
   void AddDelay_String(FIELD_PROP prop, const ByteString& str);
@@ -361,7 +361,6 @@
   void AddDelay_WordArray(FIELD_PROP prop, const std::vector<uint32_t>& array);
   void AddDelay_WideStringArray(FIELD_PROP prop,
                                 const std::vector<WideString>& array);
-
   void DoDelay();
 
   ObservedPtr<CJS_Document> m_pJSDoc;
diff --git a/fxjs/cjs_font.cpp b/fxjs/cjs_font.cpp
index 0e6e94b..fcf69ac 100644
--- a/fxjs/cjs_font.cpp
+++ b/fxjs/cjs_font.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,7 +22,7 @@
     {"Symbol", JSConstSpec::String, 0, "Symbol"},
     {"ZapfD", JSConstSpec::String, 0, "ZapfDingbats"}};
 
-int CJS_Font::ObjDefnID = -1;
+uint32_t CJS_Font::ObjDefnID = 0;
 
 // static
 void CJS_Font::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_font.h b/fxjs/cjs_font.h
index d3cf159..a4ecca1 100644
--- a/fxjs/cjs_font.h
+++ b/fxjs/cjs_font.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Font() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_global.cpp b/fxjs/cjs_global.cpp
index 8ac3181..101dee2 100644
--- a/fxjs/cjs_global.cpp
+++ b/fxjs/cjs_global.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include "fxjs/cjs_global.h"
 
-#include <map>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -15,110 +14,20 @@
 #include "fxjs/cfx_globaldata.h"
 #include "fxjs/cfx_keyvalue.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/v8-isolate.h"
 
 namespace {
 
-WideString PropFromV8Prop(v8::Isolate* pIsolate,
-                          v8::Local<v8::String> property) {
-  v8::String::Utf8Value utf8_value(pIsolate, property);
-  return WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
-}
-
-template <class Alt>
-void JSSpecialPropQuery(const char*,
-                        v8::Local<v8::String> property,
-                        const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  auto pObj = JSGetObject<Alt>(info.Holder());
-  if (!pObj)
-    return;
-
-  CJS_Runtime* pRuntime = pObj->GetRuntime();
-  if (!pRuntime)
-    return;
-
-  CJS_Result result =
-      pObj->QueryProperty(PropFromV8Prop(info.GetIsolate(), property).c_str());
-
-  info.GetReturnValue().Set(!result.HasError() ? 4 : 0);
-}
-
-template <class Alt>
-void JSSpecialPropGet(const char* class_name,
-                      v8::Local<v8::String> property,
-                      const v8::PropertyCallbackInfo<v8::Value>& info) {
-  auto pObj = JSGetObject<Alt>(info.Holder());
-  if (!pObj)
-    return;
-
-  CJS_Runtime* pRuntime = pObj->GetRuntime();
-  if (!pRuntime)
-    return;
-
-  CJS_Result result = pObj->GetProperty(
-      pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str());
-
-  if (result.HasError()) {
-    pRuntime->Error(
-        JSFormatErrorString(class_name, "GetProperty", result.Error()));
-    return;
-  }
-  if (result.HasReturn())
-    info.GetReturnValue().Set(result.Return());
-}
-
-template <class Alt>
-void JSSpecialPropPut(const char* class_name,
-                      v8::Local<v8::String> property,
-                      v8::Local<v8::Value> value,
-                      const v8::PropertyCallbackInfo<v8::Value>& info) {
-  auto pObj = JSGetObject<Alt>(info.Holder());
-  if (!pObj)
-    return;
-
-  CJS_Runtime* pRuntime = pObj->GetRuntime();
-  if (!pRuntime)
-    return;
-
-  CJS_Result result = pObj->SetProperty(
-      pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str(), value);
-
-  if (result.HasError()) {
-    pRuntime->Error(
-        JSFormatErrorString(class_name, "PutProperty", result.Error()));
-  }
-}
-
-template <class Alt>
-void JSSpecialPropDel(const char* class_name,
-                      v8::Local<v8::String> property,
-                      const v8::PropertyCallbackInfo<v8::Boolean>& info) {
-  auto pObj = JSGetObject<Alt>(info.Holder());
-  if (!pObj)
-    return;
-
-  CJS_Runtime* pRuntime = pObj->GetRuntime();
-  if (!pRuntime)
-    return;
-
-  CJS_Result result = pObj->DelProperty(
-      pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str());
-  if (result.HasError()) {
-    // TODO(dsinclair): Should this set the pRuntime->Error result?
-    // ByteString cbName =
-    //     ByteString::Format("%s.%s", class_name, "DelProperty");
-  }
-}
-
-template <class T>
-v8::Local<v8::String> GetV8StringFromProperty(v8::Local<v8::Name> property,
-                                              const T& info) {
-  return property->ToString(info.GetIsolate()->GetCurrentContext())
-      .ToLocalChecked();
+ByteString ByteStringFromV8Name(v8::Isolate* pIsolate,
+                                v8::Local<v8::Name> name) {
+  CHECK(name->IsString());
+  return fxv8::ToByteString(pIsolate, name.As<v8::String>());
 }
 
 }  // namespace
@@ -130,7 +39,7 @@
 const JSMethodSpec CJS_Global::MethodSpecs[] = {
     {"setPersistent", setPersistent_static}};
 
-int CJS_Global::ObjDefnID = -1;
+uint32_t CJS_Global::ObjDefnID = 0;
 
 // static
 void CJS_Global::setPersistent_static(
@@ -143,24 +52,36 @@
 void CJS_Global::queryprop_static(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  ASSERT(property->IsString());
-  JSSpecialPropQuery<CJS_Global>(
-      "global",
-      v8::Local<v8::String>::New(info.GetIsolate(),
-                                 GetV8StringFromProperty(property, info)),
-      info);
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
+  if (pObj->HasProperty(bsProp))
+    info.GetReturnValue().Set(static_cast<int>(v8::PropertyAttribute::None));
 }
 
 // static
 void CJS_Global::getprop_static(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  ASSERT(property->IsString());
-  JSSpecialPropGet<CJS_Global>(
-      "global",
-      v8::Local<v8::String>::New(info.GetIsolate(),
-                                 GetV8StringFromProperty(property, info)),
-      info);
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
+  CJS_Result result = pObj->GetProperty(pRuntime, bsProp);
+  if (result.HasError()) {
+    pRuntime->Error(
+        JSFormatErrorString("global", "GetProperty", result.Error()));
+    return;
+  }
+  if (result.HasReturn())
+    info.GetReturnValue().Set(result.Return());
 }
 
 // static
@@ -168,35 +89,60 @@
     v8::Local<v8::Name> property,
     v8::Local<v8::Value> value,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  ASSERT(property->IsString());
-  JSSpecialPropPut<CJS_Global>(
-      "global",
-      v8::Local<v8::String>::New(info.GetIsolate(),
-                                 GetV8StringFromProperty(property, info)),
-      value, info);
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
+  CJS_Result result = pObj->SetProperty(pRuntime, bsProp, value);
+  if (result.HasError()) {
+    pRuntime->Error(
+        JSFormatErrorString("global", "PutProperty", result.Error()));
+    return;
+  }
+  info.GetReturnValue().Set(value);
 }
 
 // static
 void CJS_Global::delprop_static(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Boolean>& info) {
-  ASSERT(property->IsString());
-  JSSpecialPropDel<CJS_Global>(
-      "global",
-      v8::Local<v8::String>::New(info.GetIsolate(),
-                                 GetV8StringFromProperty(property, info)),
-      info);
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
+  if (pObj->DelProperty(bsProp))
+    info.GetReturnValue().Set(true);
+}
+
+void CJS_Global::enumprop_static(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
+  if (!pObj)
+    return;
+
+  CJS_Runtime* pRuntime = pObj->GetRuntime();
+  if (!pRuntime)
+    return;
+
+  pObj->EnumProperties(pRuntime, info);
 }
 
 // static
 void CJS_Global::DefineAllProperties(CFXJS_Engine* pEngine) {
   pEngine->DefineObjAllProperties(
       ObjDefnID, CJS_Global::queryprop_static, CJS_Global::getprop_static,
-      CJS_Global::putprop_static, CJS_Global::delprop_static);
+      CJS_Global::putprop_static, CJS_Global::delprop_static,
+      CJS_Global::enumprop_static);
 }
 
 // static
-int CJS_Global::GetObjDefnID() {
+uint32_t CJS_Global::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -209,38 +155,32 @@
 }
 
 CJS_Global::CJS_Global(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
-    : CJS_Object(pObject, pRuntime) {
-  CPDFSDK_FormFillEnvironment* pFormFillEnv = GetRuntime()->GetFormFillEnv();
-  m_pFormFillEnv.Reset(pFormFillEnv);
-  m_pGlobalData = CFX_GlobalData::GetRetainedInstance(nullptr);
+    : CJS_Object(pObject, pRuntime),
+      m_pGlobalData(CFX_GlobalData::GetRetainedInstance(nullptr)) {
   UpdateGlobalPersistentVariables();
 }
 
 CJS_Global::~CJS_Global() {
   DestroyGlobalPersisitentVariables();
-  m_pGlobalData->Release();
+  m_pGlobalData.ExtractAsDangling()->Release();
 }
 
-CJS_Result CJS_Global::QueryProperty(const wchar_t* propname) {
-  if (WideString(propname).EqualsASCII("setPersistent"))
-    return CJS_Result::Success();
-
-  return CJS_Result::Failure(JSMessage::kUnknownProperty);
+bool CJS_Global::HasProperty(const ByteString& propname) {
+  return pdfium::Contains(m_MapGlobal, propname);
 }
 
-CJS_Result CJS_Global::DelProperty(CJS_Runtime* pRuntime,
-                                   const wchar_t* propname) {
-  auto it = m_MapGlobal.find(WideString(propname).ToDefANSI());
+bool CJS_Global::DelProperty(const ByteString& propname) {
+  auto it = m_MapGlobal.find(propname);
   if (it == m_MapGlobal.end())
-    return CJS_Result::Failure(JSMessage::kUnknownProperty);
+    return false;
 
   it->second->bDeleted = true;
-  return CJS_Result::Success();
+  return true;
 }
 
 CJS_Result CJS_Global::GetProperty(CJS_Runtime* pRuntime,
-                                   const wchar_t* propname) {
-  auto it = m_MapGlobal.find(WideString(propname).ToDefANSI());
+                                   const ByteString& propname) {
+  auto it = m_MapGlobal.find(propname);
   if (it == m_MapGlobal.end())
     return CJS_Result::Success();
 
@@ -249,17 +189,17 @@
     return CJS_Result::Success();
 
   switch (pData->nType) {
-    case CFX_Value::DataType::NUMBER:
+    case CFX_Value::DataType::kNumber:
       return CJS_Result::Success(pRuntime->NewNumber(pData->dData));
-    case CFX_Value::DataType::BOOLEAN:
+    case CFX_Value::DataType::kBoolean:
       return CJS_Result::Success(pRuntime->NewBoolean(pData->bData));
-    case CFX_Value::DataType::STRING:
-      return CJS_Result::Success(pRuntime->NewString(
-          WideString::FromDefANSI(pData->sData.AsStringView()).AsStringView()));
-    case CFX_Value::DataType::OBJECT:
+    case CFX_Value::DataType::kString:
+      return CJS_Result::Success(
+          pRuntime->NewString(pData->sData.AsStringView()));
+    case CFX_Value::DataType::kObject:
       return CJS_Result::Success(
           v8::Local<v8::Object>::New(pRuntime->GetIsolate(), pData->pData));
-    case CFX_Value::DataType::NULLOBJ:
+    case CFX_Value::DataType::kNull:
       return CJS_Result::Success(pRuntime->NewNull());
     default:
       break;
@@ -268,46 +208,60 @@
 }
 
 CJS_Result CJS_Global::SetProperty(CJS_Runtime* pRuntime,
-                                   const wchar_t* propname,
+                                   const ByteString& propname,
                                    v8::Local<v8::Value> vp) {
-  ByteString sPropName = WideString(propname).ToDefANSI();
   if (vp->IsNumber()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::NUMBER,
+    return SetGlobalVariables(propname, CFX_Value::DataType::kNumber,
                               pRuntime->ToDouble(vp), false, ByteString(),
                               v8::Local<v8::Object>(), false);
   }
   if (vp->IsBoolean()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::BOOLEAN, 0,
+    return SetGlobalVariables(propname, CFX_Value::DataType::kBoolean, 0,
                               pRuntime->ToBoolean(vp), ByteString(),
                               v8::Local<v8::Object>(), false);
   }
   if (vp->IsString()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::STRING, 0, false,
-                              pRuntime->ToWideString(vp).ToDefANSI(),
+    return SetGlobalVariables(propname, CFX_Value::DataType::kString, 0, false,
+                              pRuntime->ToByteString(vp),
                               v8::Local<v8::Object>(), false);
   }
   if (vp->IsObject()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::OBJECT, 0, false,
+    return SetGlobalVariables(propname, CFX_Value::DataType::kObject, 0, false,
                               ByteString(), pRuntime->ToObject(vp), false);
   }
   if (vp->IsNull()) {
-    return SetGlobalVariables(sPropName, CFX_Value::DataType::NULLOBJ, 0, false,
+    return SetGlobalVariables(propname, CFX_Value::DataType::kNull, 0, false,
                               ByteString(), v8::Local<v8::Object>(), false);
   }
   if (vp->IsUndefined()) {
-    DelProperty(pRuntime, propname);
+    DelProperty(propname);
     return CJS_Result::Success();
   }
   return CJS_Result::Failure(JSMessage::kObjectTypeError);
 }
 
+void CJS_Global::EnumProperties(
+    CJS_Runtime* pRuntime,
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Local<v8::Array> result = pRuntime->NewArray();
+  int idx = 0;
+  for (const auto& it : m_MapGlobal) {
+    if (it.second->bDeleted)
+      continue;
+    v8::Local<v8::Name> name = pRuntime->NewString(it.first.AsStringView());
+    pRuntime->PutArrayElement(result, idx, name);
+    ++idx;
+  }
+  info.GetReturnValue().Set(result);
+}
+
 CJS_Result CJS_Global::setPersistent(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto it = m_MapGlobal.find(pRuntime->ToWideString(params[0]).ToDefANSI());
+  auto it = m_MapGlobal.find(pRuntime->ToByteString(params[0]));
   if (it == m_MapGlobal.end() || it->second->bDeleted)
     return CJS_Result::Failure(JSMessage::kGlobalNotFoundError);
 
@@ -323,47 +277,44 @@
   for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) {
     CFX_GlobalData::Element* pData = m_pGlobalData->GetAt(i);
     switch (pData->data.nType) {
-      case CFX_Value::DataType::NUMBER:
-        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::NUMBER,
+      case CFX_Value::DataType::kNumber:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kNumber,
                            pData->data.dData, false, ByteString(),
-                           v8::Local<v8::Object>(), pData->bPersistent == 1);
+                           v8::Local<v8::Object>(), pData->bPersistent);
         pRuntime->PutObjectProperty(ToV8Object(),
                                     pData->data.sKey.AsStringView(),
                                     pRuntime->NewNumber(pData->data.dData));
         break;
-      case CFX_Value::DataType::BOOLEAN:
-        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::BOOLEAN, 0,
+      case CFX_Value::DataType::kBoolean:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kBoolean, 0,
                            pData->data.bData == 1, ByteString(),
-                           v8::Local<v8::Object>(), pData->bPersistent == 1);
+                           v8::Local<v8::Object>(), pData->bPersistent);
         pRuntime->PutObjectProperty(
             ToV8Object(), pData->data.sKey.AsStringView(),
             pRuntime->NewBoolean(pData->data.bData == 1));
         break;
-      case CFX_Value::DataType::STRING:
-        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::STRING, 0,
+      case CFX_Value::DataType::kString:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kString, 0,
                            false, pData->data.sData, v8::Local<v8::Object>(),
-                           pData->bPersistent == 1);
+                           pData->bPersistent);
         pRuntime->PutObjectProperty(
             ToV8Object(), pData->data.sKey.AsStringView(),
-            pRuntime->NewString(
-                WideString::FromUTF8(pData->data.sData.AsStringView())
-                    .AsStringView()));
+            pRuntime->NewString(pData->data.sData.AsStringView()));
         break;
-      case CFX_Value::DataType::OBJECT: {
+      case CFX_Value::DataType::kObject: {
         v8::Local<v8::Object> pObj = pRuntime->NewObject();
         if (!pObj.IsEmpty()) {
           PutObjectProperty(pObj, &pData->data);
-          SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::OBJECT, 0,
-                             false, ByteString(), pObj,
-                             pData->bPersistent == 1);
+          SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kObject, 0,
+                             false, ByteString(), pObj, pData->bPersistent);
           pRuntime->PutObjectProperty(ToV8Object(),
                                       pData->data.sKey.AsStringView(), pObj);
         }
       } break;
-      case CFX_Value::DataType::NULLOBJ:
-        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::NULLOBJ, 0,
+      case CFX_Value::DataType::kNull:
+        SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kNull, 0,
                            false, ByteString(), v8::Local<v8::Object>(),
-                           pData->bPersistent == 1);
+                           pData->bPersistent);
         pRuntime->PutObjectProperty(
             ToV8Object(), pData->data.sKey.AsStringView(), pRuntime->NewNull());
         break;
@@ -371,7 +322,11 @@
   }
 }
 
-void CJS_Global::CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime) {
+void CJS_Global::CommitGlobalPersisitentVariables() {
+  CJS_Runtime* pRuntime = GetRuntime();
+  if (!pRuntime)
+    return;
+
   for (const auto& iter : m_MapGlobal) {
     ByteString name = iter.first;
     JSGlobalData* pData = iter.second.get();
@@ -380,27 +335,26 @@
       continue;
     }
     switch (pData->nType) {
-      case CFX_Value::DataType::NUMBER:
+      case CFX_Value::DataType::kNumber:
         m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case CFX_Value::DataType::BOOLEAN:
+      case CFX_Value::DataType::kBoolean:
         m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case CFX_Value::DataType::STRING:
+      case CFX_Value::DataType::kString:
         m_pGlobalData->SetGlobalVariableString(name, pData->sData);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
-      case CFX_Value::DataType::OBJECT: {
-        std::vector<std::unique_ptr<CFX_KeyValue>> array;
+      case CFX_Value::DataType::kObject: {
         v8::Local<v8::Object> obj =
-            v8::Local<v8::Object>::New(GetIsolate(), pData->pData);
-        ObjectToArray(pRuntime, obj, &array);
-        m_pGlobalData->SetGlobalVariableObject(name, std::move(array));
+            v8::Local<v8::Object>::New(pRuntime->GetIsolate(), pData->pData);
+        m_pGlobalData->SetGlobalVariableObject(name,
+                                               ObjectToArray(pRuntime, obj));
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
       } break;
-      case CFX_Value::DataType::NULLOBJ:
+      case CFX_Value::DataType::kNull:
         m_pGlobalData->SetGlobalVariableNull(name);
         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
         break;
@@ -408,55 +362,56 @@
   }
 }
 
-void CJS_Global::ObjectToArray(
+std::vector<std::unique_ptr<CFX_KeyValue>> CJS_Global::ObjectToArray(
     CJS_Runtime* pRuntime,
-    v8::Local<v8::Object> pObj,
-    std::vector<std::unique_ptr<CFX_KeyValue>>* pArray) {
+    v8::Local<v8::Object> pObj) {
+  std::vector<std::unique_ptr<CFX_KeyValue>> array;
   std::vector<WideString> pKeyList = pRuntime->GetObjectPropertyNames(pObj);
   for (const auto& ws : pKeyList) {
     ByteString sKey = ws.ToUTF8();
     v8::Local<v8::Value> v =
         pRuntime->GetObjectProperty(pObj, sKey.AsStringView());
     if (v->IsNumber()) {
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::NUMBER;
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kNumber;
       pObjElement->sKey = sKey;
       pObjElement->dData = pRuntime->ToDouble(v);
-      pArray->push_back(std::move(pObjElement));
+      array.push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsBoolean()) {
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::BOOLEAN;
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kBoolean;
       pObjElement->sKey = sKey;
       pObjElement->dData = pRuntime->ToBoolean(v);
-      pArray->push_back(std::move(pObjElement));
+      array.push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsString()) {
-      ByteString sValue = pRuntime->ToWideString(v).ToDefANSI();
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::STRING;
+      ByteString sValue = pRuntime->ToByteString(v);
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kString;
       pObjElement->sKey = sKey;
       pObjElement->sData = sValue;
-      pArray->push_back(std::move(pObjElement));
+      array.push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsObject()) {
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::OBJECT;
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kObject;
       pObjElement->sKey = sKey;
-      ObjectToArray(pRuntime, pRuntime->ToObject(v), &pObjElement->objData);
-      pArray->push_back(std::move(pObjElement));
+      pObjElement->objData = ObjectToArray(pRuntime, pRuntime->ToObject(v));
+      array.push_back(std::move(pObjElement));
       continue;
     }
     if (v->IsNull()) {
-      auto pObjElement = pdfium::MakeUnique<CFX_KeyValue>();
-      pObjElement->nType = CFX_Value::DataType::NULLOBJ;
+      auto pObjElement = std::make_unique<CFX_KeyValue>();
+      pObjElement->nType = CFX_Value::DataType::kNull;
       pObjElement->sKey = sKey;
-      pArray->push_back(std::move(pObjElement));
+      array.push_back(std::move(pObjElement));
     }
   }
+  return array;
 }
 
 void CJS_Global::PutObjectProperty(v8::Local<v8::Object> pObj,
@@ -468,22 +423,20 @@
   for (size_t i = 0; i < pData->objData.size(); ++i) {
     CFX_KeyValue* pObjData = pData->objData.at(i).get();
     switch (pObjData->nType) {
-      case CFX_Value::DataType::NUMBER:
+      case CFX_Value::DataType::kNumber:
         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewNumber(pObjData->dData));
         break;
-      case CFX_Value::DataType::BOOLEAN:
+      case CFX_Value::DataType::kBoolean:
         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewBoolean(pObjData->bData == 1));
         break;
-      case CFX_Value::DataType::STRING:
+      case CFX_Value::DataType::kString:
         pRuntime->PutObjectProperty(
             pObj, pObjData->sKey.AsStringView(),
-            pRuntime->NewString(
-                WideString::FromUTF8(pObjData->sData.AsStringView())
-                    .AsStringView()));
+            pRuntime->NewString(pObjData->sData.AsStringView()));
         break;
-      case CFX_Value::DataType::OBJECT: {
+      case CFX_Value::DataType::kObject: {
         v8::Local<v8::Object> pNewObj = pRuntime->NewObject();
         if (!pNewObj.IsEmpty()) {
           PutObjectProperty(pNewObj, pObjData);
@@ -491,7 +444,7 @@
                                       pNewObj);
         }
       } break;
-      case CFX_Value::DataType::NULLOBJ:
+      case CFX_Value::DataType::kNull:
         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
                                     pRuntime->NewNull());
         break;
@@ -518,25 +471,25 @@
     JSGlobalData* pTemp = it->second.get();
     if (pTemp->bDeleted || pTemp->nType != nType) {
       pTemp->dData = 0;
-      pTemp->bData = 0;
+      pTemp->bData = false;
       pTemp->sData.clear();
       pTemp->nType = nType;
     }
     pTemp->bDeleted = false;
     switch (nType) {
-      case CFX_Value::DataType::NUMBER:
+      case CFX_Value::DataType::kNumber:
         pTemp->dData = dData;
         break;
-      case CFX_Value::DataType::BOOLEAN:
+      case CFX_Value::DataType::kBoolean:
         pTemp->bData = bData;
         break;
-      case CFX_Value::DataType::STRING:
+      case CFX_Value::DataType::kString:
         pTemp->sData = sData;
         break;
-      case CFX_Value::DataType::OBJECT:
+      case CFX_Value::DataType::kObject:
         pTemp->pData.Reset(pData->GetIsolate(), pData);
         break;
-      case CFX_Value::DataType::NULLOBJ:
+      case CFX_Value::DataType::kNull:
         break;
       default:
         return CJS_Result::Failure(JSMessage::kObjectTypeError);
@@ -544,30 +497,30 @@
     return CJS_Result::Success();
   }
 
-  auto pNewData = pdfium::MakeUnique<JSGlobalData>();
+  auto pNewData = std::make_unique<JSGlobalData>();
   switch (nType) {
-    case CFX_Value::DataType::NUMBER:
-      pNewData->nType = CFX_Value::DataType::NUMBER;
+    case CFX_Value::DataType::kNumber:
+      pNewData->nType = CFX_Value::DataType::kNumber;
       pNewData->dData = dData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case CFX_Value::DataType::BOOLEAN:
-      pNewData->nType = CFX_Value::DataType::BOOLEAN;
+    case CFX_Value::DataType::kBoolean:
+      pNewData->nType = CFX_Value::DataType::kBoolean;
       pNewData->bData = bData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case CFX_Value::DataType::STRING:
-      pNewData->nType = CFX_Value::DataType::STRING;
+    case CFX_Value::DataType::kString:
+      pNewData->nType = CFX_Value::DataType::kString;
       pNewData->sData = sData;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case CFX_Value::DataType::OBJECT:
-      pNewData->nType = CFX_Value::DataType::OBJECT;
+    case CFX_Value::DataType::kObject:
+      pNewData->nType = CFX_Value::DataType::kObject;
       pNewData->pData.Reset(pData->GetIsolate(), pData);
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    case CFX_Value::DataType::NULLOBJ:
-      pNewData->nType = CFX_Value::DataType::NULLOBJ;
+    case CFX_Value::DataType::kNull:
+      pNewData->nType = CFX_Value::DataType::kNull;
       pNewData->bPersistent = bDefaultPersistent;
       break;
     default:
diff --git a/fxjs/cjs_global.h b/fxjs/cjs_global.h
index 2c89188..944c030 100644
--- a/fxjs/cjs_global.h
+++ b/fxjs/cjs_global.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,15 +11,26 @@
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cfx_keyvalue.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_result.h"
 
 class CFX_GlobalData;
 
+// The CJS_Global object is not the V8 global object (i.e. it is not |this|
+// in JavaScript outside of a bound function call). It is a facility for
+// sharing data amongst documents and persisting data within a document
+// between sessions. It is only partially implemented due to security and
+// privacy concerns. It provides access via properties in the usual manner,
+// execpt that these are stored on the C++ side rather than in V8 itself.
+// It is a static object that is available as "global" property of the V8
+// global object and can be manipulated from JavaScript as |global['foo']|
+// for example.
+
 class CJS_Global final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
   static void DefineAllProperties(CFXJS_Engine* pEngine);
 
@@ -33,6 +44,7 @@
                              const v8::PropertyCallbackInfo<v8::Value>& info);
   static void delprop_static(v8::Local<v8::Name> property,
                              const v8::PropertyCallbackInfo<v8::Boolean>& info);
+  static void enumprop_static(const v8::PropertyCallbackInfo<v8::Array>& info);
 
   static void setPersistent_static(
       const v8::FunctionCallbackInfo<v8::Value>& info);
@@ -40,16 +52,6 @@
   CJS_Global(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
   ~CJS_Global() override;
 
-  CJS_Result DelProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
-
-  CJS_Result setPersistent(CJS_Runtime* pRuntime,
-                           const std::vector<v8::Local<v8::Value>>& params);
-  CJS_Result QueryProperty(const wchar_t* propname);
-  CJS_Result GetProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
-  CJS_Result SetProperty(CJS_Runtime* pRuntime,
-                         const wchar_t* propname,
-                         v8::Local<v8::Value> vp);
-
  private:
   struct JSGlobalData : public CFX_Value {
    public:
@@ -61,11 +63,12 @@
     bool bDeleted = false;
   };
 
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSMethodSpec MethodSpecs[];
 
   void UpdateGlobalPersistentVariables();
-  void CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime);
+  // TODO(crbug.com/pdfium/926): This method is never called.
+  void CommitGlobalPersisitentVariables();
   void DestroyGlobalPersisitentVariables();
   CJS_Result SetGlobalVariables(const ByteString& propname,
                                 CFX_Value::DataType nType,
@@ -74,15 +77,23 @@
                                 const ByteString& sData,
                                 v8::Local<v8::Object> pData,
                                 bool bDefaultPersistent);
-  void ObjectToArray(CJS_Runtime* pRuntime,
-                     v8::Local<v8::Object> pObj,
-                     std::vector<std::unique_ptr<CFX_KeyValue>>* pArray);
+  std::vector<std::unique_ptr<CFX_KeyValue>> ObjectToArray(
+      CJS_Runtime* pRuntime,
+      v8::Local<v8::Object> pObj);
   void PutObjectProperty(v8::Local<v8::Object> obj, CFX_KeyValue* pData);
+  CJS_Result setPersistent(CJS_Runtime* pRuntime,
+                           const std::vector<v8::Local<v8::Value>>& params);
+  bool HasProperty(const ByteString& propname);
+  bool DelProperty(const ByteString& propname);
+  CJS_Result GetProperty(CJS_Runtime* pRuntime, const ByteString& propname);
+  CJS_Result SetProperty(CJS_Runtime* pRuntime,
+                         const ByteString& propname,
+                         v8::Local<v8::Value> vp);
+  void EnumProperties(CJS_Runtime* pRuntime,
+                      const v8::PropertyCallbackInfo<v8::Array>& info);
 
   std::map<ByteString, std::unique_ptr<JSGlobalData>> m_MapGlobal;
-  WideString m_sFilePath;
-  CFX_GlobalData* m_pGlobalData;
-  ObservedPtr<CPDFSDK_FormFillEnvironment> m_pFormFillEnv;
+  UnownedPtr<CFX_GlobalData> m_pGlobalData;
 };
 
 #endif  // FXJS_CJS_GLOBAL_H_
diff --git a/fxjs/cjs_globalarrays.cpp b/fxjs/cjs_globalarrays.cpp
index 9cf540e..3dbf62c 100644
--- a/fxjs/cjs_globalarrays.cpp
+++ b/fxjs/cjs_globalarrays.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,32 @@
 
 #include "fxjs/cjs_globalarrays.h"
 
-#define GLOBAL_ARRAY(rt, name, ...)                                          \
-  {                                                                          \
-    static const wchar_t* const values[] = {__VA_ARGS__};                    \
-    v8::Local<v8::Array> array = (rt)->NewArray();                           \
-    v8::Local<v8::Context> ctx = (rt)->GetIsolate()->GetCurrentContext();    \
-    for (size_t i = 0; i < FX_ArraySize(values); ++i)                        \
-      array->Set(ctx, i, (rt)->NewString(values[i])).FromJust();             \
-    (rt)->SetConstArray((name), array);                                      \
-    (rt)->DefineGlobalConst(                                                 \
-        (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {        \
-          CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder());  \
-          CJS_Runtime* pCurrentRuntime = pObj->GetRuntime();                 \
-          if (pCurrentRuntime)                                               \
-            info.GetReturnValue().Set(pCurrentRuntime->GetConstArray(name)); \
-        });                                                                  \
+#include <iterator>
+
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-isolate.h"
+
+#define GLOBAL_ARRAY(rt, name, ...)                                            \
+  {                                                                            \
+    static const wchar_t* const values[] = {__VA_ARGS__};                      \
+    v8::Local<v8::Array> array = (rt)->NewArray();                             \
+    v8::Local<v8::Context> ctx = (rt)->GetIsolate()->GetCurrentContext();      \
+    for (size_t i = 0; i < std::size(values); ++i) {                           \
+      array                                                                    \
+          ->Set(ctx, pdfium::base::checked_cast<uint32_t>(i),                  \
+                (rt)->NewString(values[i]))                                    \
+          .FromJust();                                                         \
+    }                                                                          \
+    (rt)->SetConstArray((name), array);                                        \
+    (rt)->DefineGlobalConst(                                                   \
+        (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {          \
+          CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.GetIsolate(), \
+                                                            info.Holder());    \
+          CJS_Runtime* pCurrentRuntime = pObj->GetRuntime();                   \
+          if (pCurrentRuntime)                                                 \
+            info.GetReturnValue().Set(pCurrentRuntime->GetConstArray(name));   \
+        });                                                                    \
   }
 
 // static
diff --git a/fxjs/cjs_globalarrays.h b/fxjs/cjs_globalarrays.h
index 244db2e..726e3a7 100644
--- a/fxjs/cjs_globalarrays.h
+++ b/fxjs/cjs_globalarrays.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/cjs_globalconsts.cpp b/fxjs/cjs_globalconsts.cpp
index cb6bd33..854ded8 100644
--- a/fxjs/cjs_globalconsts.cpp
+++ b/fxjs/cjs_globalconsts.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,14 +6,13 @@
 
 #include "fxjs/cjs_globalconsts.h"
 
-#define GLOBAL_STRING(rt, name, value)                                        \
-  (rt)->DefineGlobalConst(                                                    \
-      (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) {           \
-        const char* pStr = (value);                                           \
-        info.GetReturnValue().Set(                                            \
-            v8::String::NewFromUtf8(info.GetIsolate(), pStr,                  \
-                                    v8::NewStringType::kNormal, strlen(pStr)) \
-                .ToLocalChecked());                                           \
+#include "fxjs/fxv8.h"
+
+#define GLOBAL_STRING(rt, name, value)                              \
+  (rt)->DefineGlobalConst(                                          \
+      (name), [](const v8::FunctionCallbackInfo<v8::Value>& info) { \
+        info.GetReturnValue().Set(                                  \
+            fxv8::NewStringHelper(info.GetIsolate(), (value)));     \
       })
 
 // static
diff --git a/fxjs/cjs_globalconsts.h b/fxjs/cjs_globalconsts.h
index 71c689b..f553f3e 100644
--- a/fxjs/cjs_globalconsts.h
+++ b/fxjs/cjs_globalconsts.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/cjs_highlight.cpp b/fxjs/cjs_highlight.cpp
index 4ada397..174685d 100644
--- a/fxjs/cjs_highlight.cpp
+++ b/fxjs/cjs_highlight.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
     {"p", JSConstSpec::String, 0, "push"},
     {"o", JSConstSpec::String, 0, "outline"}};
 
-int CJS_Highlight::ObjDefnID = -1;
+uint32_t CJS_Highlight::ObjDefnID = 0;
 
 // static
 void CJS_Highlight::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_highlight.h b/fxjs/cjs_highlight.h
index efec127..ad68811 100644
--- a/fxjs/cjs_highlight.h
+++ b/fxjs/cjs_highlight.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Highlight() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_icon.cpp b/fxjs/cjs_icon.cpp
index 80be5c8..d3b9da4 100644
--- a/fxjs/cjs_icon.cpp
+++ b/fxjs/cjs_icon.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,11 @@
 const JSPropertySpec CJS_Icon::PropertySpecs[] = {
     {"name", get_name_static, set_name_static}};
 
-int CJS_Icon::ObjDefnID = -1;
+uint32_t CJS_Icon::ObjDefnID = 0;
 const char CJS_Icon::kName[] = "Icon";
 
 // static
-int CJS_Icon::GetObjDefnID() {
+uint32_t CJS_Icon::GetObjDefnID() {
   return ObjDefnID;
 }
 
diff --git a/fxjs/cjs_icon.h b/fxjs/cjs_icon.h
index 321f508..54dc1cb 100644
--- a/fxjs/cjs_icon.h
+++ b/fxjs/cjs_icon.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
 
 class CJS_Icon final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Icon(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -24,7 +24,7 @@
   JS_STATIC_PROP(name, name, CJS_Icon)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSPropertySpec PropertySpecs[];
 
diff --git a/fxjs/cjs_object.cpp b/fxjs/cjs_object.cpp
index e13a509..f27d37d 100644
--- a/fxjs/cjs_object.cpp
+++ b/fxjs/cjs_object.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,11 +10,11 @@
 
 // static
 void CJS_Object::DefineConsts(CFXJS_Engine* pEngine,
-                              int objId,
+                              uint32_t nObjDefnID,
                               pdfium::span<const JSConstSpec> consts) {
   for (const auto& item : consts) {
     pEngine->DefineObjConst(
-        objId, item.pName,
+        nObjDefnID, item.pName,
         item.eType == JSConstSpec::Number
             ? pEngine->NewNumber(item.number).As<v8::Value>()
             : pEngine->NewString(item.pStr).As<v8::Value>());
@@ -23,23 +23,22 @@
 
 // static
 void CJS_Object::DefineProps(CFXJS_Engine* pEngine,
-                             int objId,
+                             uint32_t nObjDefnID,
                              pdfium::span<const JSPropertySpec> props) {
   for (const auto& item : props)
-    pEngine->DefineObjProperty(objId, item.pName, item.pPropGet, item.pPropPut);
+    pEngine->DefineObjProperty(nObjDefnID, item.pName, item.pPropGet,
+                               item.pPropPut);
 }
 
 // static
 void CJS_Object::DefineMethods(CFXJS_Engine* pEngine,
-                               int objId,
+                               uint32_t nObjDefnID,
                                pdfium::span<const JSMethodSpec> methods) {
   for (const auto& item : methods)
-    pEngine->DefineObjMethod(objId, item.pName, item.pMethodCall);
+    pEngine->DefineObjMethod(nObjDefnID, item.pName, item.pMethodCall);
 }
 
 CJS_Object::CJS_Object(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
-    : m_pIsolate(pObject->GetIsolate()),
-      m_pV8Object(GetIsolate(), pObject),
-      m_pRuntime(pRuntime) {}
+    : m_pV8Object(pObject->GetIsolate(), pObject), m_pRuntime(pRuntime) {}
 
-CJS_Object::~CJS_Object() {}
+CJS_Object::~CJS_Object() = default;
diff --git a/fxjs/cjs_object.h b/fxjs/cjs_object.h
index 5da72ff..c53568e 100644
--- a/fxjs/cjs_object.h
+++ b/fxjs/cjs_object.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -36,24 +36,24 @@
 class CJS_Object {
  public:
   static void DefineConsts(CFXJS_Engine* pEngine,
-                           int objId,
+                           uint32_t nObjDefnID,
                            pdfium::span<const JSConstSpec> consts);
   static void DefineProps(CFXJS_Engine* pEngine,
-                          int objId,
+                          uint32_t nObjDefnID,
                           pdfium::span<const JSPropertySpec> consts);
   static void DefineMethods(CFXJS_Engine* pEngine,
-                            int objId,
+                            uint32_t nObjDefnID,
                             pdfium::span<const JSMethodSpec> consts);
 
   CJS_Object(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
   virtual ~CJS_Object();
 
-  v8::Local<v8::Object> ToV8Object() { return m_pV8Object.Get(GetIsolate()); }
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  v8::Local<v8::Object> ToV8Object() {
+    return m_pV8Object.Get(GetRuntime()->GetIsolate());
+  }
   CJS_Runtime* GetRuntime() const { return m_pRuntime.Get(); }
 
  private:
-  UnownedPtr<v8::Isolate> m_pIsolate;
   v8::Global<v8::Object> m_pV8Object;
   ObservedPtr<CJS_Runtime> m_pRuntime;
 };
diff --git a/fxjs/cjs_position.cpp b/fxjs/cjs_position.cpp
index dc9594e..ccf4fe6 100644
--- a/fxjs/cjs_position.cpp
+++ b/fxjs/cjs_position.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,7 @@
     {"textIconH", JSConstSpec::Number, 5, 0},
     {"overlay", JSConstSpec::Number, 6, 0}};
 
-int CJS_Position::ObjDefnID = -1;
+uint32_t CJS_Position::ObjDefnID = 0;
 
 // static
 void CJS_Position::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_position.h b/fxjs/cjs_position.h
index ae951bf..14097a9 100644
--- a/fxjs/cjs_position.h
+++ b/fxjs/cjs_position.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Position() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_publicmethods.cpp b/fxjs/cjs_publicmethods.cpp
index 8eed97d..07fe8c5 100644
--- a/fxjs/cjs_publicmethods.cpp
+++ b/fxjs/cjs_publicmethods.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,9 @@
 
 #include "fxjs/cjs_publicmethods.h"
 
+#include <math.h>
+
 #include <algorithm>
-#include <cmath>
-#include <cwctype>
 #include <iomanip>
 #include <iterator>
 #include <limits>
@@ -18,15 +18,16 @@
 #include <vector>
 
 #include "build/build_config.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfdoc/cpdf_formcontrol.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "core/fxge/cfx_color.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "fxjs/cjs_color.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_runtime.h"
@@ -34,7 +35,10 @@
 #include "fxjs/fx_date_helpers.h"
 #include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-container.h"
 
 // static
 const JSMethodSpec CJS_PublicMethods::GlobalFunctionSpecs[] = {
@@ -64,7 +68,7 @@
 
 namespace {
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 constexpr double kDoubleCorrect = 0.000000000000001;
 #endif
 
@@ -93,14 +97,17 @@
   return result;
 }
 
-void AlertIfPossible(CJS_EventContext* pContext, const WideString& swMsg) {
+void AlertIfPossible(CJS_EventContext* pContext,
+                     const WideString& wsCaller,
+                     const WideString& wsMsg) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv = pContext->GetFormFillEnv();
-  if (pFormFillEnv)
-    pFormFillEnv->JS_appAlert(swMsg, WideString(), JSPLATFORM_ALERT_BUTTON_OK,
+  if (pFormFillEnv) {
+    pFormFillEnv->JS_appAlert(wsMsg, wsCaller, JSPLATFORM_ALERT_BUTTON_OK,
                               JSPLATFORM_ALERT_ICON_STATUS);
+  }
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 ByteString CalculateString(double dValue,
                            int iDec,
                            int* iDec2,
@@ -112,16 +119,17 @@
   // Make sure the number of precision characters will fit.
   iDec = std::min(iDec, std::numeric_limits<double>::digits10);
 
-  std::stringstream ss;
+  fxcrt::ostringstream ss;
   ss << std::fixed << std::setprecision(iDec) << dValue;
-  std::string value = ss.str();
+  fxcrt::string value = ss.str();
   size_t pos = value.find('.');
-  *iDec2 = pos == std::string::npos ? value.size() : static_cast<int>(pos);
+  *iDec2 = pdfium::base::checked_cast<int>(
+      pos == fxcrt::string::npos ? value.size() : pos);
   return ByteString(value.c_str());
 }
 #endif
 
-WideString CalcMergedString(const CJS_EventRecorder* event,
+WideString CalcMergedString(const CJS_EventContext* event,
                             const WideString& value,
                             const WideString& change) {
   WideString prefix = value.First(event->SelStart());
@@ -136,7 +144,8 @@
                           const std::vector<v8::Local<v8::Value>>&)>
 void JSGlobalFunc(const char* func_name_string,
                   const v8::FunctionCallbackInfo<v8::Value>& info) {
-  CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder());
+  CJS_Object* pObj =
+      CFXJS_Engine::GetObjectPrivate(info.GetIsolate(), info.Holder());
   if (!pObj)
     return;
 
@@ -171,13 +180,13 @@
   return c == '.' || c == ',';
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 bool IsStyleWithDigitSeparator(int style) {
   return style == 0 || style == 2;
 }
 
 char DigitSeparatorForStyle(int style) {
-  ASSERT(IsStyleWithDigitSeparator(style));
+  DCHECK(IsStyleWithDigitSeparator(style));
   return style == 0 ? ',' : '.';
 }
 
@@ -194,7 +203,7 @@
   return IsStyleWithCommaDecimalMark(style) ? ',' : '.';
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 void NormalizeDecimalMark(ByteString* str) {
   str->Replace(",", ".");
 }
@@ -204,20 +213,20 @@
   str->Replace(L",", L".");
 }
 
-Optional<double> ApplyNamedOperation(const wchar_t* sFunction,
-                                     double dValue1,
-                                     double dValue2) {
-  if (FXSYS_wcsicmp(sFunction, L"AVG") == 0 ||
-      FXSYS_wcsicmp(sFunction, L"SUM") == 0) {
+absl::optional<double> ApplyNamedOperation(const WideString& wsFunction,
+                                           double dValue1,
+                                           double dValue2) {
+  if (wsFunction.EqualsASCIINoCase("AVG") ||
+      wsFunction.EqualsASCIINoCase("SUM")) {
     return dValue1 + dValue2;
   }
-  if (FXSYS_wcsicmp(sFunction, L"PRD") == 0)
+  if (wsFunction.EqualsASCIINoCase("PRD"))
     return dValue1 * dValue2;
-  if (FXSYS_wcsicmp(sFunction, L"MIN") == 0)
+  if (wsFunction.EqualsASCIINoCase("MIN"))
     return std::min(dValue1, dValue2);
-  if (FXSYS_wcsicmp(sFunction, L"MAX") == 0)
+  if (wsFunction.EqualsASCIINoCase("MAX"))
     return std::max(dValue1, dValue2);
-  return {};
+  return absl::nullopt;
 }
 
 }  // namespace
@@ -313,14 +322,13 @@
 v8::Local<v8::Array> CJS_PublicMethods::AF_MakeArrayFromList(
     CJS_Runtime* pRuntime,
     v8::Local<v8::Value> val) {
-  ASSERT(!val.IsEmpty());
+  DCHECK(!val.IsEmpty());
   if (val->IsArray())
     return pRuntime->ToArray(val);
 
-  ASSERT(val->IsString());
-  WideString wsStr = pRuntime->ToWideString(val);
-  ByteString t = wsStr.ToDefANSI();
-  const char* p = t.c_str();
+  DCHECK(val->IsString());
+  ByteString bsVal = pRuntime->ToByteString(val);
+  const char* p = bsVal.c_str();
 
   int nIndex = 0;
   v8::Local<v8::Array> StrArray = pRuntime->NewArray();
@@ -343,7 +351,8 @@
   return StrArray;
 }
 
-double CJS_PublicMethods::ParseDate(const WideString& value,
+double CJS_PublicMethods::ParseDate(v8::Isolate* isolate,
+                                    const WideString& value,
                                     bool* bWrongFormat) {
   double dt = FX_GetDateTime();
   int nYear = FX_GetYearFromTime(dt);
@@ -417,26 +426,28 @@
   }
 
   // TODO(thestig): Should we set |bWrongFormat| to false here too?
-  return JS_DateParse(WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay,
-                                         nYear, nHour, nMin, nSec));
+  return JS_DateParse(
+      isolate, WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay, nYear,
+                                  nHour, nMin, nSec));
 }
 
-double CJS_PublicMethods::ParseDateUsingFormat(const WideString& value,
+double CJS_PublicMethods::ParseDateUsingFormat(v8::Isolate* isolate,
+                                               const WideString& value,
                                                const WideString& format,
                                                bool* bWrongFormat) {
-  double dRet = std::nan("");
+  double dRet = nan("");
   fxjs::ConversionStatus status = FX_ParseDateUsingFormat(value, format, &dRet);
   if (status == fxjs::ConversionStatus::kSuccess)
     return dRet;
 
   if (status == fxjs::ConversionStatus::kBadDate) {
-    dRet = JS_DateParse(value);
-    if (!std::isnan(dRet))
+    dRet = JS_DateParse(isolate, value);
+    if (!isnan(dRet))
       return dRet;
   }
 
   bool bBadFormat = false;
-  dRet = ParseDate(value, &bBadFormat);
+  dRet = ParseDate(isolate, value, &bBadFormat);
   if (bWrongFormat)
     *bWrongFormat = bBadFormat;
 
@@ -475,23 +486,23 @@
               sPart += c;
               break;
             case 'm':
-              sPart = WideString::Format(L"%d", nMonth);
+              sPart = WideString::FormatInteger(nMonth);
               break;
             case 'd':
-              sPart = WideString::Format(L"%d", nDay);
+              sPart = WideString::FormatInteger(nDay);
               break;
             case 'H':
-              sPart = WideString::Format(L"%d", nHour);
+              sPart = WideString::FormatInteger(nHour);
               break;
             case 'h':
               sPart =
-                  WideString::Format(L"%d", nHour > 12 ? nHour - 12 : nHour);
+                  WideString::FormatInteger(nHour > 12 ? nHour - 12 : nHour);
               break;
             case 'M':
-              sPart = WideString::Format(L"%d", nMin);
+              sPart = WideString::FormatInteger(nMin);
               break;
             case 's':
-              sPart = WideString::Format(L"%d", nSec);
+              sPart = WideString::FormatInteger(nSec);
               break;
             case 't':
               sPart += nHour > 12 ? 'p' : 'a';
@@ -582,17 +593,16 @@
 CJS_Result CJS_PublicMethods::AFNumber_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   if (params.size() != 6)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CJS_EventContext* pEventContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pEventContext->GetEventRecorder();
-  if (!pEvent->HasValue())
+  if (!pEventContext->HasValue())
     return CJS_Result::Failure(WideString::FromASCII("No event handler"));
 
-  WideString& Value = pEvent->Value();
-  ByteString strValue = StrTrim(Value.ToDefANSI());
+  WideString& Value = pEventContext->Value();
+  ByteString strValue = StrTrim(Value.ToUTF8());
   if (strValue.IsEmpty())
     return CJS_Result::Success();
 
@@ -621,7 +631,7 @@
       iDec2 = 1;
     }
   }
-  ASSERT(iDec2 >= 0);
+  DCHECK(iDec2 >= 0);
 
   // Processing separator style
   if (static_cast<size_t>(iDec2) < strValue.GetLength()) {
@@ -638,7 +648,7 @@
   }
 
   // Processing currency string
-  Value = WideString::FromDefANSI(strValue.AsStringView());
+  Value = WideString::FromUTF8(strValue.AsStringView());
   if (bCurrencyPrepend)
     Value = wstrCurrency + Value;
   else
@@ -694,24 +704,23 @@
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
-  if (!pEvent->HasValue())
+  if (!pContext->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
-  WideString& val = pEvent->Value();
-  WideString& wstrChange = pEvent->Change();
+  WideString& val = pContext->Value();
+  WideString& wstrChange = pContext->Change();
   WideString wstrValue = val;
 
-  if (pEvent->WillCommit()) {
+  if (pContext->WillCommit()) {
     WideString swTemp = StrTrim(wstrValue);
     if (swTemp.IsEmpty())
       return CJS_Result::Success();
 
     NormalizeDecimalMarkW(&swTemp);
     if (!IsNumber(swTemp)) {
-      pEvent->Rc() = false;
+      pContext->Rc() = false;
       WideString sError = JSGetStringFromID(JSMessage::kInvalidInputError);
-      AlertIfPossible(pContext, sError);
+      AlertIfPossible(pContext, L"AFNumber_Keystroke", sError);
       return CJS_Result::Failure(sError);
     }
     // It happens after the last keystroke and before validating,
@@ -719,16 +728,16 @@
   }
 
   WideString wstrSelected;
-  if (pEvent->SelStart() != -1) {
-    wstrSelected = wstrValue.Substr(pEvent->SelStart(),
-                                    pEvent->SelEnd() - pEvent->SelStart());
+  if (pContext->SelStart() != -1) {
+    wstrSelected = wstrValue.Substr(pContext->SelStart(),
+                                    pContext->SelEnd() - pContext->SelStart());
   }
 
   bool bHasSign = wstrValue.Contains(L'-') && !wstrSelected.Contains(L'-');
   if (bHasSign) {
     // can't insert "change" in front of sign position.
-    if (!wstrSelected.IsEmpty() && pEvent->SelStart() == 0) {
-      pEvent->Rc() = false;
+    if (!wstrSelected.IsEmpty() && pContext->SelStart() == 0) {
+      pContext->Rc() = false;
       return CJS_Result::Success();
     }
   }
@@ -740,7 +749,7 @@
   for (size_t i = 0; i < wstrChange.GetLength(); ++i) {
     if (wstrChange[i] == cSep) {
       if (bHasSep) {
-        pEvent->Rc() = false;
+        pContext->Rc() = false;
         return CJS_Result::Success();
       }
       bHasSep = true;
@@ -748,16 +757,16 @@
     }
     if (wstrChange[i] == L'-') {
       if (bHasSign) {
-        pEvent->Rc() = false;
+        pContext->Rc() = false;
         return CJS_Result::Success();
       }
       // sign's position is not correct
       if (i != 0) {
-        pEvent->Rc() = false;
+        pContext->Rc() = false;
         return CJS_Result::Success();
       }
-      if (pEvent->SelStart() != 0) {
-        pEvent->Rc() = false;
+      if (pContext->SelStart() != 0) {
+        pContext->Rc() = false;
         return CJS_Result::Success();
       }
       bHasSign = true;
@@ -765,12 +774,12 @@
     }
 
     if (!FXSYS_IsDecimalDigit(wstrChange[i])) {
-      pEvent->Rc() = false;
+      pContext->Rc() = false;
       return CJS_Result::Success();
     }
   }
 
-  val = CalcMergedString(pEvent, wstrValue, wstrChange);
+  val = CalcMergedString(pContext, wstrValue, wstrChange);
   return CJS_Result::Success();
 }
 
@@ -778,12 +787,11 @@
 CJS_Result CJS_PublicMethods::AFPercent_Format(
     CJS_Runtime* pRuntime,
     const std::vector<v8::Local<v8::Value>>& params) {
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   if (params.size() < 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -799,8 +807,7 @@
 
   // When the |iDec| value is too big, Acrobat will just return "%".
   static constexpr int kDecLimit = 512;
-  // TODO(thestig): Calculate this once C++14 can be used to declare variables
-  // in constexpr functions.
+  // This count must be in sync with |kDecLimit|.
   static constexpr size_t kDigitsInDecLimit = 3;
   WideString& Value = pEvent->Value();
   if (iDec > kDecLimit) {
@@ -808,7 +815,7 @@
     return CJS_Result::Success();
   }
 
-  ByteString strValue = StrTrim(Value.ToDefANSI());
+  ByteString strValue = StrTrim(Value.ToUTF8());
   if (strValue.IsEmpty())
     strValue = "0";
 
@@ -839,7 +846,7 @@
   strValue.ReleaseBuffer(szNewSize);
 
   // for processing separator style
-  Optional<size_t> mark_pos = strValue.Find('.');
+  absl::optional<size_t> mark_pos = strValue.Find('.');
   if (mark_pos.has_value()) {
     char mark = DecimalMarkForStyle(iSepStyle);
     if (mark != '.')
@@ -859,7 +866,7 @@
     strValue.InsertAtFront('%');
   else
     strValue.InsertAtBack('%');
-  Value = WideString::FromDefANSI(strValue.AsStringView());
+  Value = WideString::FromUTF8(strValue.AsStringView());
 #endif
   return CJS_Result::Success();
 }
@@ -878,8 +885,7 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -892,15 +898,16 @@
   double dDate;
   if (strValue.Contains(L"GMT")) {
     // e.g. "Tue Aug 11 14:24:16 GMT+08002009"
-    dDate = ParseDateAsGMT(strValue);
+    dDate = ParseDateAsGMT(pRuntime->GetIsolate(), strValue);
   } else {
-    dDate = ParseDateUsingFormat(strValue, sFormat, nullptr);
+    dDate = ParseDateUsingFormat(pRuntime->GetIsolate(), strValue, sFormat,
+                                 nullptr);
   }
 
-  if (std::isnan(dDate)) {
+  if (isnan(dDate)) {
     WideString swMsg = WideString::Format(
         JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
-    AlertIfPossible(pContext, swMsg);
+    AlertIfPossible(pEvent, L"AFDate_FormatEx", swMsg);
     return CJS_Result::Failure(JSMessage::kParseDateError);
   }
 
@@ -908,7 +915,8 @@
   return CJS_Result::Success();
 }
 
-double CJS_PublicMethods::ParseDateAsGMT(const WideString& strValue) {
+double CJS_PublicMethods::ParseDateAsGMT(v8::Isolate* isolate,
+                                         const WideString& strValue) {
   std::vector<WideString> wsArray;
   WideString sTemp;
   for (const auto& c : strValue) {
@@ -924,9 +932,9 @@
 
   int nMonth = 1;
   sTemp = wsArray[1];
-  for (size_t i = 0; i < FX_ArraySize(fxjs::kMonths); ++i) {
-    if (sTemp.Compare(fxjs::kMonths[i]) == 0) {
-      nMonth = i + 1;
+  for (size_t i = 0; i < std::size(fxjs::kMonths); ++i) {
+    if (sTemp == fxjs::kMonths[i]) {
+      nMonth = static_cast<int>(i) + 1;
       break;
     }
   }
@@ -938,8 +946,8 @@
   int nYear = StringToFloat(wsArray[7].AsStringView());
   double dRet = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
                             FX_MakeTime(nHour, nMin, nSec, 0));
-  if (std::isnan(dRet))
-    dRet = JS_DateParse(strValue);
+  if (isnan(dRet))
+    dRet = JS_DateParse(isolate, strValue);
 
   return dRet;
 }
@@ -953,8 +961,7 @@
         "AFDate_KeystrokeEx's parameter size not correct"));
   }
 
-  CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->WillCommit())
     return CJS_Result::Success();
 
@@ -967,11 +974,12 @@
 
   bool bWrongFormat = false;
   WideString sFormat = pRuntime->ToWideString(params[0]);
-  double dRet = ParseDateUsingFormat(strValue, sFormat, &bWrongFormat);
-  if (bWrongFormat || std::isnan(dRet)) {
+  double dRet = ParseDateUsingFormat(pRuntime->GetIsolate(), strValue, sFormat,
+                                     &bWrongFormat);
+  if (bWrongFormat || isnan(dRet)) {
     WideString swMsg = WideString::Format(
         JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
-    AlertIfPossible(pContext, swMsg);
+    AlertIfPossible(pEvent, L"AFDate_KeystrokeEx", swMsg);
     pEvent->Rc() = false;
   }
   return CJS_Result::Success();
@@ -983,8 +991,8 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
-                                  FX_ArraySize(kDateFormats));
+  int iIndex =
+      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kDateFormats));
   std::vector<v8::Local<v8::Value>> newParams;
   newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
   return AFDate_FormatEx(pRuntime, newParams);
@@ -997,8 +1005,8 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
-                                  FX_ArraySize(kDateFormats));
+  int iIndex =
+      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kDateFormats));
   std::vector<v8::Local<v8::Value>> newParams;
   newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
   return AFDate_KeystrokeEx(pRuntime, newParams);
@@ -1011,8 +1019,8 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
-                                  FX_ArraySize(kTimeFormats));
+  int iIndex =
+      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kTimeFormats));
   std::vector<v8::Local<v8::Value>> newParams;
   newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
   return AFDate_FormatEx(pRuntime, newParams);
@@ -1024,8 +1032,8 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]),
-                                  FX_ArraySize(kTimeFormats));
+  int iIndex =
+      WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kTimeFormats));
   std::vector<v8::Local<v8::Value>> newParams;
   newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
   return AFDate_KeystrokeEx(pRuntime, newParams);
@@ -1050,8 +1058,7 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1086,8 +1093,7 @@
   if (params.size() < 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1101,7 +1107,7 @@
       return CJS_Result::Success();
 
     if (valEvent.GetLength() > wstrMask.GetLength()) {
-      AlertIfPossible(pContext,
+      AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
                       JSGetStringFromID(JSMessage::kParamTooLongError));
       pEvent->Rc() = false;
       return CJS_Result::Success();
@@ -1113,7 +1119,7 @@
         break;
     }
     if (iIndex != wstrMask.GetLength()) {
-      AlertIfPossible(pContext,
+      AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
                       JSGetStringFromID(JSMessage::kInvalidInputError));
       pEvent->Rc() = false;
     }
@@ -1129,20 +1135,22 @@
   size_t combined_len = valEvent.GetLength() + wChange.GetLength() +
                         pEvent->SelStart() - pEvent->SelEnd();
   if (combined_len > wstrMask.GetLength()) {
-    AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError));
+    AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
+                    JSGetStringFromID(JSMessage::kParamTooLongError));
     pEvent->Rc() = false;
     return CJS_Result::Success();
   }
 
   if (iIndexMask >= wstrMask.GetLength() && !wChange.IsEmpty()) {
-    AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError));
+    AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
+                    JSGetStringFromID(JSMessage::kParamTooLongError));
     pEvent->Rc() = false;
     return CJS_Result::Success();
   }
 
   for (size_t i = 0; i < wChange.GetLength(); ++i) {
     if (iIndexMask >= wstrMask.GetLength()) {
-      AlertIfPossible(pContext,
+      AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
                       JSGetStringFromID(JSMessage::kParamTooLongError));
       pEvent->Rc() = false;
       return CJS_Result::Success();
@@ -1168,8 +1176,7 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventRecorder* pEvent =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
@@ -1203,19 +1210,17 @@
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventRecorder* pEventRecorder =
-      pRuntime->GetCurrentEventContext()->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
 
   WideString swValue;
-  if (pEventRecorder->HasValue())
-    swValue = pEventRecorder->Value();
+  if (pEvent->HasValue())
+    swValue = pEvent->Value();
 
-  if (pEventRecorder->WillCommit())
+  if (pEvent->WillCommit())
     return CJS_Result::Success(pRuntime->NewString(swValue.AsStringView()));
 
   return CJS_Result::Success(pRuntime->NewString(
-      CalcMergedString(pEventRecorder, swValue, pEventRecorder->Change())
-          .AsStringView()));
+      CalcMergedString(pEvent, swValue, pEvent->Change()).AsStringView()));
 }
 
 CJS_Result CJS_PublicMethods::AFParseDateEx(
@@ -1226,11 +1231,13 @@
 
   WideString sValue = pRuntime->ToWideString(params[0]);
   WideString sFormat = pRuntime->ToWideString(params[1]);
-  double dDate = ParseDateUsingFormat(sValue, sFormat, nullptr);
-  if (std::isnan(dDate)) {
+  double dDate =
+      ParseDateUsingFormat(pRuntime->GetIsolate(), sValue, sFormat, nullptr);
+  if (isnan(dDate)) {
     WideString swMsg = WideString::Format(
         JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str());
-    AlertIfPossible(pRuntime->GetCurrentEventContext(), swMsg);
+    AlertIfPossible(pRuntime->GetCurrentEventContext(), L"AFParseDateEx",
+                    swMsg);
     return CJS_Result::Failure(JSMessage::kParseDateError);
   }
   return CJS_Result::Success(pRuntime->NewNumber(dDate));
@@ -1245,10 +1252,10 @@
   WideString sFunction = pRuntime->ToWideString(params[0]);
   double arg1 = pRuntime->ToDouble(params[1]);
   double arg2 = pRuntime->ToDouble(params[2]);
-  if (std::isnan(arg1) || std::isnan(arg2))
+  if (isnan(arg1) || isnan(arg2))
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  Optional<double> result = ApplyNamedOperation(sFunction.c_str(), arg1, arg2);
+  absl::optional<double> result = ApplyNamedOperation(sFunction, arg1, arg2);
   if (!result.has_value())
     return CJS_Result::Failure(JSMessage::kValueError);
 
@@ -1347,8 +1354,8 @@
            wcscmp(sFunction.c_str(), L"MAX") == 0)) {
         dValue = dTemp;
       }
-      Optional<double> dResult =
-          ApplyNamedOperation(sFunction.c_str(), dValue, dTemp);
+      absl::optional<double> dResult =
+          ApplyNamedOperation(sFunction, dValue, dTemp);
       if (!dResult.has_value())
         return CJS_Result::Failure(JSMessage::kValueError);
 
@@ -1360,13 +1367,11 @@
   if (wcscmp(sFunction.c_str(), L"AVG") == 0 && nFieldsCount > 0)
     dValue /= nFieldsCount;
 
-  dValue = floor(dValue * FXSYS_pow(10, 6) + 0.49) / FXSYS_pow(10, 6);
+  dValue = floor(dValue * powf(10, 6) + 0.49) / powf(10, 6);
 
   CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  if (pContext->GetEventRecorder()->HasValue()) {
-    pContext->GetEventRecorder()->Value() =
-        pRuntime->ToWideString(pRuntime->NewNumber(dValue));
-  }
+  if (pContext->HasValue())
+    pContext->Value() = pRuntime->ToWideString(pRuntime->NewNumber(dValue));
 
   return CJS_Result::Success();
 }
@@ -1379,15 +1384,14 @@
   if (params.size() != 4)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
-  CJS_EventRecorder* pEvent = pContext->GetEventRecorder();
+  CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
   if (!pEvent->HasValue())
     return CJS_Result::Failure(JSMessage::kBadObjectError);
 
   if (pEvent->Value().IsEmpty())
     return CJS_Result::Success();
 
-  double dEentValue = atof(pEvent->Value().ToDefANSI().c_str());
+  double dEventValue = atof(pEvent->Value().ToUTF8().c_str());
   bool bGreaterThan = pRuntime->ToBoolean(params[0]);
   double dGreaterThan = pRuntime->ToDouble(params[1]);
   bool bLessThan = pRuntime->ToBoolean(params[2]);
@@ -1395,25 +1399,25 @@
   WideString swMsg;
 
   if (bGreaterThan && bLessThan) {
-    if (dEentValue < dGreaterThan || dEentValue > dLessThan)
+    if (dEventValue < dGreaterThan || dEventValue > dLessThan)
       swMsg = WideString::Format(
           JSGetStringFromID(JSMessage::kRangeBetweenError).c_str(),
           pRuntime->ToWideString(params[1]).c_str(),
           pRuntime->ToWideString(params[3]).c_str());
   } else if (bGreaterThan) {
-    if (dEentValue < dGreaterThan)
+    if (dEventValue < dGreaterThan)
       swMsg = WideString::Format(
           JSGetStringFromID(JSMessage::kRangeGreaterError).c_str(),
           pRuntime->ToWideString(params[1]).c_str());
   } else if (bLessThan) {
-    if (dEentValue > dLessThan)
+    if (dEventValue > dLessThan)
       swMsg = WideString::Format(
           JSGetStringFromID(JSMessage::kRangeLessError).c_str(),
           pRuntime->ToWideString(params[3]).c_str());
   }
 
   if (!swMsg.IsEmpty()) {
-    AlertIfPossible(pContext, swMsg);
+    AlertIfPossible(pEvent, L"AFRange_Validate", swMsg);
     pEvent->Rc() = false;
   }
   return CJS_Result::Success();
diff --git a/fxjs/cjs_publicmethods.h b/fxjs/cjs_publicmethods.h
index 53a4dcc..ddf13cd 100644
--- a/fxjs/cjs_publicmethods.h
+++ b/fxjs/cjs_publicmethods.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,9 +18,12 @@
 
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
-  static double ParseDate(const WideString& value, bool* bWrongFormat);
-  static double ParseDateAsGMT(const WideString& value);
-  static double ParseDateUsingFormat(const WideString& value,
+  static double ParseDate(v8::Isolate* isolate,
+                          const WideString& value,
+                          bool* bWrongFormat);
+  static double ParseDateAsGMT(v8::Isolate* isolate, const WideString& value);
+  static double ParseDateUsingFormat(v8::Isolate* isolate,
+                                     const WideString& value,
                                      const WideString& format,
                                      bool* bWrongFormat);
 
diff --git a/fxjs/cjs_publicmethods_embeddertest.cpp b/fxjs/cjs_publicmethods_embeddertest.cpp
index d35f0cc..5a1a6e7 100644
--- a/fxjs/cjs_publicmethods_embeddertest.cpp
+++ b/fxjs/cjs_publicmethods_embeddertest.cpp
@@ -1,16 +1,22 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cmath>
+#include <math.h>
+
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fxjs/cjs_event_context.h"
 #include "fxjs/cjs_publicmethods.h"
+#include "testing/external_engine_embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/js_embedder_test.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-value.h"
 
 namespace {
 
@@ -20,7 +26,7 @@
 
 }  // namespace
 
-class CJS_PublicMethodsEmbedderTest : public JSEmbedderTest {};
+class CJS_PublicMethodsEmbedderTest : public ExternalEngineEmbedderTest {};
 
 TEST_F(CJS_PublicMethodsEmbedderTest, ParseDateUsingFormat) {
   v8::Isolate::Scope isolate_scope(isolate());
@@ -31,80 +37,80 @@
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"06/25/1968", L"mm/dd/yyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"06/25/1968",
+                                                 L"mm/dd/yyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"25061968", L"ddmmyyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"25061968",
+                                                 L"ddmmyyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1968
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"19680625", L"yyyymmdd",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"19680625",
+                                                 L"yyyymmdd", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(-47865600000, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1985
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"31121985", L"ddmmyyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"31121985",
+                                                 L"ddmmyyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(504835200000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2085, the other '85.
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"311285", L"ddmmyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"311285",
+                                                 L"ddmmyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(3660595200000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 1995
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"01021995", L"ddmmyyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"01021995",
+                                                 L"ddmmyyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(791596800000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2095, the other '95.
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"010295", L"ddmmyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"010295",
+                                                 L"ddmmyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(3947356800000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2005
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"01022005", L"ddmmyyyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"01022005",
+                                                 L"ddmmyyyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(1107216000000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2005
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"010205", L"ddmmyy",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"010205",
+                                                 L"ddmmyy", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(1107216000000.0, date);
   EXPECT_FALSE(bWrongFormat);
 
   // 2005 in a different format. https://crbug.com/436572
   bWrongFormat = false;
-  date = CJS_PublicMethods::ParseDateUsingFormat(L"050201", L"yymmdd",
-                                                 &bWrongFormat);
+  date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"050201",
+                                                 L"yymmdd", &bWrongFormat);
   date = RoundDownDate(date);
   EXPECT_DOUBLE_EQ(1107216000000.0, date);
   EXPECT_FALSE(bWrongFormat);
@@ -182,7 +188,7 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  EXPECT_TRUE(OpenDocument("calculate.pdf"));
+  ASSERT_TRUE(OpenDocument("calculate.pdf"));
   auto* page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -191,8 +197,7 @@
   runtime.NewEventContext();
 
   WideString result;
-  runtime.GetCurrentEventContext()->GetEventRecorder()->SetValueForTest(
-      &result);
+  runtime.GetCurrentEventContext()->SetValueForTest(&result);
 
   auto ary = runtime.NewArray();
   runtime.PutArrayElement(ary, 0, runtime.NewString("Calc1_A"));
@@ -205,8 +210,7 @@
   CJS_Result ret = CJS_PublicMethods::AFSimple_Calculate(&runtime, params);
   UnloadPage(page);
 
-  runtime.GetCurrentEventContext()->GetEventRecorder()->SetValueForTest(
-      nullptr);
+  runtime.GetCurrentEventContext()->SetValueForTest(nullptr);
 
   ASSERT_TRUE(!ret.HasError());
   ASSERT_TRUE(!ret.HasReturn());
@@ -218,7 +222,7 @@
   v8::HandleScope handle_scope(isolate());
   v8::Context::Scope context_scope(GetV8Context());
 
-  EXPECT_TRUE(OpenDocument("calculate.pdf"));
+  ASSERT_TRUE(OpenDocument("calculate.pdf"));
   auto* page = LoadPage(0);
   ASSERT_TRUE(page);
 
@@ -226,7 +230,7 @@
       CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()));
   runtime.NewEventContext();
 
-  auto* handler = runtime.GetCurrentEventContext()->GetEventRecorder();
+  auto* handler = runtime.GetCurrentEventContext();
 
   bool valid = true;
   WideString result = L"-10";
diff --git a/fxjs/cjs_publicmethods_unittest.cpp b/fxjs/cjs_publicmethods_unittest.cpp
index 1d72418..a5b9314 100644
--- a/fxjs/cjs_publicmethods_unittest.cpp
+++ b/fxjs/cjs_publicmethods_unittest.cpp
@@ -1,9 +1,11 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/cjs_publicmethods.h"
 
+#include <iterator>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(CJS_PublicMethods, IsNumber) {
@@ -42,7 +44,7 @@
       {L"0123", true},
       {L"9876123", true},
   };
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
+  for (size_t i = 0; i < std::size(test_data); ++i) {
     EXPECT_EQ(test_data[i].expected,
               CJS_PublicMethods::IsNumber(test_data[i].input))
         << "for case " << i;
diff --git a/fxjs/cjs_result.cpp b/fxjs/cjs_result.cpp
index 92f2b0b..8bed633 100644
--- a/fxjs/cjs_result.cpp
+++ b/fxjs/cjs_result.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,7 @@
 
 #include "fxjs/cjs_result.h"
 
-CJS_Result::CJS_Result() {}
+CJS_Result::CJS_Result() = default;
 
 CJS_Result::CJS_Result(v8::Local<v8::Value> ret) : return_(ret) {}
 
diff --git a/fxjs/cjs_result.h b/fxjs/cjs_result.h
index 5e1ed7c..993a4dc 100644
--- a/fxjs/cjs_result.h
+++ b/fxjs/cjs_result.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,12 +8,12 @@
 #define FXJS_CJS_RESULT_H_
 
 #include "fxjs/js_resources.h"
-#include "third_party/base/optional.h"
-#include "v8/include/v8.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/v8-forward.h"
 
 class CJS_Result {
  public:
-  // Wrap constructors with static methods so we can apply WARN_UNUSED_RESULT,
+  // Wrap constructors with static methods so we can apply [[nodiscard]],
   // otherwise we can't catch places where someone mistakenly writes:
   //
   //     if (error)
@@ -24,14 +24,14 @@
   //     if (error)
   //       return CJS_Result(JS_ERROR_CODE);
   //
-  static CJS_Result Success() WARN_UNUSED_RESULT { return CJS_Result(); }
-  static CJS_Result Success(v8::Local<v8::Value> value) WARN_UNUSED_RESULT {
+  [[nodiscard]] static CJS_Result Success() { return CJS_Result(); }
+  [[nodiscard]] static CJS_Result Success(v8::Local<v8::Value> value) {
     return CJS_Result(value);
   }
-  static CJS_Result Failure(const WideString& str) WARN_UNUSED_RESULT {
+  [[nodiscard]] static CJS_Result Failure(const WideString& str) {
     return CJS_Result(str);
   }
-  static CJS_Result Failure(JSMessage id) WARN_UNUSED_RESULT {
+  [[nodiscard]] static CJS_Result Failure(JSMessage id) {
     return CJS_Result(id);
   }
 
@@ -39,7 +39,7 @@
   ~CJS_Result();
 
   bool HasError() const { return error_.has_value(); }
-  WideString Error() const { return error_.value(); }
+  const WideString& Error() const { return error_.value(); }
 
   bool HasReturn() const { return !return_.IsEmpty(); }
   v8::Local<v8::Value> Return() const { return return_; }
@@ -50,7 +50,7 @@
   explicit CJS_Result(const WideString&);     // Error with custom message.
   explicit CJS_Result(JSMessage id);          // Error with stock message.
 
-  Optional<WideString> error_;
+  absl::optional<WideString> error_;
   v8::Local<v8::Value> return_;
 };
 
diff --git a/fxjs/cjs_runtime.cpp b/fxjs/cjs_runtime.cpp
index 919d47a..f7d76da 100644
--- a/fxjs/cjs_runtime.cpp
+++ b/fxjs/cjs_runtime.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "fxjs/cjs_runtime.h"
 
+#include <math.h>
+
 #include <algorithm>
 
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
@@ -19,7 +21,6 @@
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_event.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_field.h"
 #include "fxjs/cjs_font.h"
 #include "fxjs/cjs_global.h"
@@ -36,14 +37,20 @@
 #include "fxjs/cjs_timerobj.h"
 #include "fxjs/cjs_util.h"
 #include "fxjs/cjs_zoomtype.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_define.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-isolate.h"
 
 CJS_Runtime::CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv)
     : m_pFormFillEnv(pFormFillEnv) {
   v8::Isolate* pIsolate = nullptr;
   IPDF_JSPLATFORM* pPlatform = m_pFormFillEnv->GetFormFillInfo()->m_pJsPlatform;
   if (pPlatform->version <= 2) {
+    // Backwards compatibility - JS now initialized earlier in more modern
+    // JSPLATFORM versions.
     unsigned int embedderDataSlot = 0;
     v8::Isolate* pExternalIsolate = nullptr;
     if (pPlatform->version == 2) {
@@ -120,12 +127,12 @@
 }
 
 IJS_EventContext* CJS_Runtime::NewEventContext() {
-  m_EventContextArray.push_back(pdfium::MakeUnique<CJS_EventContext>(this));
+  m_EventContextArray.push_back(std::make_unique<CJS_EventContext>(this));
   return m_EventContextArray.back().get();
 }
 
 void CJS_Runtime::ReleaseEventContext(IJS_EventContext* pContext) {
-  ASSERT(pContext == m_EventContextArray.back().get());
+  DCHECK_EQ(pContext, m_EventContextArray.back().get());
   m_EventContextArray.pop_back();
 }
 
@@ -134,7 +141,7 @@
                                      : m_EventContextArray.back().get();
 }
 
-TimerHandlerIface* CJS_Runtime::GetTimerHandler() const {
+CFX_Timer::HandlerIface* CJS_Runtime::GetTimerHandler() const {
   return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
 }
 
@@ -148,7 +155,7 @@
   if (pThis.IsEmpty())
     return;
 
-  auto pJSDocument = JSGetObject<CJS_Document>(pThis);
+  auto pJSDocument = JSGetObject<CJS_Document>(GetIsolate(), pThis);
   if (!pJSDocument)
     return;
 
@@ -159,7 +166,7 @@
   return m_pFormFillEnv.Get();
 }
 
-Optional<IJS_Runtime::JS_Error> CJS_Runtime::ExecuteScript(
+absl::optional<IJS_Runtime::JS_Error> CJS_Runtime::ExecuteScript(
     const WideString& script) {
   return Execute(script);
 }
@@ -176,22 +183,16 @@
   return this;
 }
 
-bool CJS_Runtime::GetValueByNameFromGlobalObject(ByteStringView utf8Name,
-                                                 v8::Local<v8::Value>* pValue) {
+v8::Local<v8::Value> CJS_Runtime::GetValueByNameFromGlobalObject(
+    ByteStringView utf8Name) {
   v8::Isolate::Scope isolate_scope(GetIsolate());
   v8::Local<v8::Context> context = GetV8Context();
   v8::Context::Scope context_scope(context);
-  v8::Local<v8::String> str =
-      v8::String::NewFromUtf8(GetIsolate(), utf8Name.unterminated_c_str(),
-                              v8::NewStringType::kNormal, utf8Name.GetLength())
-          .ToLocalChecked();
-  v8::MaybeLocal<v8::Value> maybe_propvalue =
-      context->Global()->Get(context, str);
-  if (maybe_propvalue.IsEmpty())
-    return false;
-
-  *pValue = maybe_propvalue.ToLocalChecked();
-  return true;
+  v8::Local<v8::String> str = fxv8::NewStringHelper(GetIsolate(), utf8Name);
+  v8::MaybeLocal<v8::Value> maybe_value = context->Global()->Get(context, str);
+  if (maybe_value.IsEmpty())
+    return v8::Local<v8::Value>();
+  return maybe_value.ToLocalChecked();
 }
 
 bool CJS_Runtime::SetValueByNameInGlobalObject(ByteStringView utf8Name,
@@ -203,10 +204,7 @@
   v8::Isolate::Scope isolate_scope(pIsolate);
   v8::Local<v8::Context> context = GetV8Context();
   v8::Context::Scope context_scope(context);
-  v8::Local<v8::String> str =
-      v8::String::NewFromUtf8(pIsolate, utf8Name.unterminated_c_str(),
-                              v8::NewStringType::kNormal, utf8Name.GetLength())
-          .ToLocalChecked();
+  v8::Local<v8::String> str = fxv8::NewStringHelper(pIsolate, utf8Name);
   v8::Maybe<bool> result = context->Global()->Set(context, str, pValue);
   return result.IsJust() && result.FromJust();
 }
@@ -215,22 +213,21 @@
     v8::Local<v8::Value> value) {
   bool bAllowNaN = false;
   if (value->IsString()) {
-    ByteString bstr = ToWideString(value).ToDefANSI();
+    ByteString bstr = fxv8::ToByteString(GetIsolate(), value.As<v8::String>());
     if (bstr.IsEmpty())
       return value;
     if (bstr == "NaN")
       bAllowNaN = true;
   }
 
-  v8::Isolate* pIsolate = GetIsolate();
-  v8::TryCatch try_catch(pIsolate);
+  v8::TryCatch try_catch(GetIsolate());
   v8::MaybeLocal<v8::Number> maybeNum =
-      value->ToNumber(pIsolate->GetCurrentContext());
+      value->ToNumber(GetIsolate()->GetCurrentContext());
   if (maybeNum.IsEmpty())
     return value;
 
   v8::Local<v8::Number> num = maybeNum.ToLocalChecked();
-  if (std::isnan(num->Value()) && !bAllowNaN)
+  if (isnan(num->Value()) && !bAllowNaN)
     return value;
 
   return num;
diff --git a/fxjs/cjs_runtime.h b/fxjs/cjs_runtime.h
index b3f8ef6..a3569f6 100644
--- a/fxjs/cjs_runtime.h
+++ b/fxjs/cjs_runtime.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,20 +12,19 @@
 #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/cjs_event_context.h"
 #include "fxjs/ijs_runtime.h"
 
-class CJS_EventContext;
 class CPDFSDK_FormFillEnvironment;
 
 class CJS_Runtime final : public IJS_Runtime,
                           public CFXJS_Engine,
                           public Observable {
  public:
-  using FieldEvent = std::pair<WideString, JS_EVENT_T>;
+  using FieldEvent = std::pair<WideString, CJS_EventContext::Kind>;
 
   explicit CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv);
   ~CJS_Runtime() override;
@@ -35,11 +34,11 @@
   IJS_EventContext* NewEventContext() override;
   void ReleaseEventContext(IJS_EventContext* pContext) override;
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override;
-  Optional<IJS_Runtime::JS_Error> ExecuteScript(
+  absl::optional<IJS_Runtime::JS_Error> ExecuteScript(
       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);
@@ -53,8 +52,7 @@
   // value will be returned, otherwise |value| is returned.
   v8::Local<v8::Value> MaybeCoerceToNumber(v8::Local<v8::Value> value);
 
-  bool GetValueByNameFromGlobalObject(ByteStringView utf8Name,
-                                      v8::Local<v8::Value>* pValue);
+  v8::Local<v8::Value> GetValueByNameFromGlobalObject(ByteStringView utf8Name);
   bool SetValueByNameInGlobalObject(ByteStringView utf8Name,
                                     v8::Local<v8::Value> pValue);
 
diff --git a/fxjs/cjs_runtimestub.cpp b/fxjs/cjs_runtimestub.cpp
index 6e313de..0a68a17 100644
--- a/fxjs/cjs_runtimestub.cpp
+++ b/fxjs/cjs_runtimestub.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include "fxjs/cjs_runtimestub.h"
 
 #include "fxjs/cjs_event_context_stub.h"
-#include "third_party/base/ptr_util.h"
 
 CJS_RuntimeStub::CJS_RuntimeStub(CPDFSDK_FormFillEnvironment* pFormFillEnv)
     : m_pFormFillEnv(pFormFillEnv) {}
@@ -16,21 +15,21 @@
 
 IJS_EventContext* CJS_RuntimeStub::NewEventContext() {
   if (!m_pContext)
-    m_pContext = pdfium::MakeUnique<CJS_EventContextStub>();
+    m_pContext = std::make_unique<CJS_EventContextStub>();
   return m_pContext.get();
 }
 
 void CJS_RuntimeStub::ReleaseEventContext(IJS_EventContext* pContext) {}
 
 CPDFSDK_FormFillEnvironment* CJS_RuntimeStub::GetFormFillEnv() const {
-  return m_pFormFillEnv.Get();
+  return m_pFormFillEnv;
 }
 
 CJS_Runtime* CJS_RuntimeStub::AsCJSRuntime() {
   return nullptr;
 }
 
-Optional<IJS_Runtime::JS_Error> CJS_RuntimeStub::ExecuteScript(
+absl::optional<IJS_Runtime::JS_Error> CJS_RuntimeStub::ExecuteScript(
     const WideString& script) {
-  return pdfium::nullopt;
+  return absl::nullopt;
 }
diff --git a/fxjs/cjs_runtimestub.h b/fxjs/cjs_runtimestub.h
index 2b5e713..c58e2c4 100644
--- a/fxjs/cjs_runtimestub.h
+++ b/fxjs/cjs_runtimestub.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,8 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
 #include "fxjs/ijs_runtime.h"
 
 class CPDFSDK_FormFillEnvironment;
@@ -27,7 +27,7 @@
   void ReleaseEventContext(IJS_EventContext* pContext) override;
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override;
 
-  Optional<IJS_Runtime::JS_Error> ExecuteScript(
+  absl::optional<IJS_Runtime::JS_Error> ExecuteScript(
       const WideString& script) override;
 
  private:
diff --git a/fxjs/cjs_scalehow.cpp b/fxjs/cjs_scalehow.cpp
index 999949c..b64da9f 100644
--- a/fxjs/cjs_scalehow.cpp
+++ b/fxjs/cjs_scalehow.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,7 @@
     {"proportional", JSConstSpec::Number, 0, 0},
     {"anamorphic", JSConstSpec::Number, 1, 0}};
 
-int CJS_ScaleHow::ObjDefnID = -1;
+uint32_t CJS_ScaleHow::ObjDefnID = 0;
 
 // static
 void CJS_ScaleHow::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_scalehow.h b/fxjs/cjs_scalehow.h
index f8c7eb5..b5ab3e5 100644
--- a/fxjs/cjs_scalehow.h
+++ b/fxjs/cjs_scalehow.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_ScaleHow() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_scalewhen.cpp b/fxjs/cjs_scalewhen.cpp
index 5f39153..e8a3dd4 100644
--- a/fxjs/cjs_scalewhen.cpp
+++ b/fxjs/cjs_scalewhen.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,7 +12,7 @@
     {"tooBig", JSConstSpec::Number, 2, 0},
     {"tooSmall", JSConstSpec::Number, 3, 0}};
 
-int CJS_ScaleWhen::ObjDefnID = -1;
+uint32_t CJS_ScaleWhen::ObjDefnID = 0;
 
 // static
 void CJS_ScaleWhen::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_scalewhen.h b/fxjs/cjs_scalewhen.h
index ef046f9..43ab541 100644
--- a/fxjs/cjs_scalewhen.h
+++ b/fxjs/cjs_scalewhen.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_ScaleWhen() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_style.cpp b/fxjs/cjs_style.cpp
index c068702..a43fa7d 100644
--- a/fxjs/cjs_style.cpp
+++ b/fxjs/cjs_style.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
     {"st", JSConstSpec::String, 0, "star"},
     {"sq", JSConstSpec::String, 0, "square"}};
 
-int CJS_Style::ObjDefnID = -1;
+uint32_t CJS_Style::ObjDefnID = 0;
 
 // static
 void CJS_Style::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_style.h b/fxjs/cjs_style.h
index 6e3ee2f..02449a6 100644
--- a/fxjs/cjs_style.h
+++ b/fxjs/cjs_style.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Style() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/cjs_timerobj.cpp b/fxjs/cjs_timerobj.cpp
index 5fa1ac3..9dc8cfa 100644
--- a/fxjs/cjs_timerobj.cpp
+++ b/fxjs/cjs_timerobj.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,10 +9,10 @@
 #include "fxjs/global_timer.h"
 #include "fxjs/js_define.h"
 
-int CJS_TimerObj::ObjDefnID = -1;
+uint32_t CJS_TimerObj::ObjDefnID = 0;
 
 // static
-int CJS_TimerObj::GetObjDefnID() {
+uint32_t CJS_TimerObj::GetObjDefnID() {
   return ObjDefnID;
 }
 
diff --git a/fxjs/cjs_timerobj.h b/fxjs/cjs_timerobj.h
index 69effa6..a82ecd1 100644
--- a/fxjs/cjs_timerobj.h
+++ b/fxjs/cjs_timerobj.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,7 @@
 
 class CJS_TimerObj final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_TimerObj(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -23,7 +23,7 @@
   int GetTimerID() const { return m_nTimerID; }
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
 
   int m_nTimerID = 0;  // Weak reference to GlobalTimer through global map.
 };
diff --git a/fxjs/cjs_util.cpp b/fxjs/cjs_util.cpp
index 1f95c31..812e12b 100644
--- a/fxjs/cjs_util.cpp
+++ b/fxjs/cjs_util.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,25 +6,27 @@
 
 #include "fxjs/cjs_util.h"
 
+#include <math.h>
 #include <time.h>
 
 #include <algorithm>
-#include <cmath>
-#include <cwctype>
+#include <string>
 #include <vector>
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
 #include "fxjs/cjs_event_context.h"
-#include "fxjs/cjs_eventrecorder.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/cjs_publicmethods.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/fx_date_helpers.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_define.h"
 #include "fxjs/js_resources.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-date.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include <ctype.h>
 #endif
 
@@ -40,8 +42,8 @@
 // Map PDF-style directives lacking direct wcsftime directives to
 // the value with which they will be replaced.
 struct TbConvertAdditional {
-  const wchar_t* lpszJSMark;
-  int iValue;
+  wchar_t js_mark;
+  int value;
 };
 
 const TbConvert TbConvertTable[] = {
@@ -49,7 +51,7 @@
     {L"ddd", L"%a"},  {L"dd", L"%d"},  {L"yyyy", L"%Y"}, {L"yy", L"%y"},
     {L"HH", L"%H"},   {L"hh", L"%I"},  {L"MM", L"%M"},   {L"ss", L"%S"},
     {L"TT", L"%p"},
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     {L"tt", L"%p"},   {L"h", L"%#I"},
 #else
     {L"tt", L"%P"},   {L"h", L"%l"},
@@ -75,11 +77,11 @@
     {"scand", scand_static},
     {"byteToChar", byteToChar_static}};
 
-int CJS_Util::ObjDefnID = -1;
+uint32_t CJS_Util::ObjDefnID = 0;
 const char CJS_Util::kName[] = "util";
 
 // static
-int CJS_Util::GetObjDefnID() {
+uint32_t CJS_Util::GetObjDefnID() {
   return ObjDefnID;
 }
 
@@ -109,7 +111,8 @@
   {
     size_t offset = 0;
     while (true) {
-      Optional<size_t> offset_end = unsafe_fmt_string.Find(L"%", offset + 1);
+      absl::optional<size_t> offset_end =
+          unsafe_fmt_string.Find(L"%", offset + 1);
       if (!offset_end.has_value()) {
         unsafe_conversion_specifiers.push_back(
             unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset));
@@ -132,14 +135,14 @@
 
     WideString segment;
     switch (ParseDataType(&fmt)) {
-      case UTIL_INT:
+      case DataType::kInt:
         segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i]));
         break;
-      case UTIL_DOUBLE:
+      case DataType::kDouble:
         segment =
             WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i]));
         break;
-      case UTIL_STRING:
+      case DataType::kString:
         segment = WideString::Format(fmt.c_str(),
                                      pRuntime->ToWideString(params[i]).c_str());
         break;
@@ -163,11 +166,11 @@
   if (iSize < 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  if (params[1].IsEmpty() || !params[1]->IsDate())
+  if (!fxv8::IsDate(params[1]))
     return CJS_Result::Failure(JSMessage::kSecondParamNotDateError);
 
   v8::Local<v8::Date> v8_date = params[1].As<v8::Date>();
-  if (v8_date.IsEmpty() || std::isnan(pRuntime->ToDouble(v8_date)))
+  if (v8_date.IsEmpty() || isnan(pRuntime->ToDouble(v8_date)))
     return CJS_Result::Failure(JSMessage::kSecondParamInvalidDateError);
 
   double date = FX_LocalTime(pRuntime->ToDouble(v8_date));
@@ -209,18 +212,19 @@
 
   // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
   // pre-existing %-directives before inserting our own.
-  std::basic_string<wchar_t> cFormat =
-      pRuntime->ToWideString(params[0]).c_str();
+  std::wstring cFormat = pRuntime->ToWideString(params[0]).c_str();
   cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
                 cFormat.end());
 
-  for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) {
-    int iStart = 0;
-    int iEnd;
-    while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) != -1) {
-      cFormat.replace(iEnd, wcslen(TbConvertTable[i].lpszJSMark),
+  for (size_t i = 0; i < std::size(TbConvertTable); ++i) {
+    size_t nFound = 0;
+    while (true) {
+      nFound = cFormat.find(TbConvertTable[i].lpszJSMark, nFound);
+      if (nFound == std::wstring::npos)
+        break;
+
+      cFormat.replace(nFound, wcslen(TbConvertTable[i].lpszJSMark),
                       TbConvertTable[i].lpszCppMark);
-      iStart = iEnd;
     }
   }
 
@@ -228,24 +232,24 @@
     return CJS_Result::Failure(JSMessage::kValueError);
 
   const TbConvertAdditional cTableAd[] = {
-      {L"m", month}, {L"d", day},
-      {L"H", hour},  {L"h", hour > 12 ? hour - 12 : hour},
-      {L"M", min},   {L"s", sec},
+      {L'm', month}, {L'd', day},
+      {L'H', hour},  {L'h', hour > 12 ? hour - 12 : hour},
+      {L'M', min},   {L's', sec},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) {
-    int iStart = 0;
-    int iEnd;
-    while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) {
-      if (iEnd > 0) {
-        if (cFormat[iEnd - 1] == L'%') {
-          iStart = iEnd + 1;
-          continue;
-        }
+  for (size_t i = 0; i < std::size(cTableAd); ++i) {
+    size_t nFound = 0;
+    while (true) {
+      nFound = cFormat.find(cTableAd[i].js_mark, nFound);
+      if (nFound == std::wstring::npos)
+        break;
+
+      if (nFound != 0 && cFormat[nFound - 1] == L'%') {
+        ++nFound;
+        continue;
       }
-      cFormat.replace(iEnd, wcslen(cTableAd[i].lpszJSMark),
-                      WideString::Format(L"%d", cTableAd[i].iValue).c_str());
-      iStart = iEnd;
+      cFormat.replace(nFound, 1,
+                      WideString::FormatInteger(cTableAd[i].value).c_str());
     }
   }
 
@@ -373,8 +377,9 @@
   WideString sDate = pRuntime->ToWideString(params[1]);
   double dDate = FX_GetDateTime();
   if (sDate.GetLength() > 0)
-    dDate = CJS_PublicMethods::ParseDateUsingFormat(sDate, sFormat, nullptr);
-  if (std::isnan(dDate))
+    dDate = CJS_PublicMethods::ParseDateUsingFormat(pRuntime->GetIsolate(),
+                                                    sDate, sFormat, nullptr);
+  if (isnan(dDate))
     return CJS_Result::Success(pRuntime->NewUndefined());
 
   return CJS_Result::Success(pRuntime->NewDate(dDate));
@@ -395,80 +400,80 @@
 }
 
 // static
-int CJS_Util::ParseDataType(WideString* sFormat) {
-  enum State { BEFORE, FLAGS, WIDTH, PRECISION, SPECIFIER, AFTER };
+CJS_Util::DataType CJS_Util::ParseDataType(WideString* sFormat) {
+  enum State { kBefore, kFlags, kWidth, kPrecision, kSpecifier, kAfter };
 
-  int result = -1;
-  State state = BEFORE;
+  DataType result = DataType::kInvalid;
+  State state = kBefore;
   size_t precision_digits = 0;
   size_t i = 0;
   while (i < sFormat->GetLength()) {
     wchar_t c = (*sFormat)[i];
     switch (state) {
-      case BEFORE:
+      case kBefore:
         if (c == L'%')
-          state = FLAGS;
+          state = kFlags;
         break;
-      case FLAGS:
+      case kFlags:
         if (c == L'+' || c == L'-' || c == L'#' || c == L' ') {
           // Stay in same state.
         } else {
-          state = WIDTH;
+          state = kWidth;
           continue;  // Re-process same character.
         }
         break;
-      case WIDTH:
+      case kWidth:
         if (c == L'*')
-          return -1;
+          return DataType::kInvalid;
         if (FXSYS_IsDecimalDigit(c)) {
           // Stay in same state.
         } else if (c == L'.') {
-          state = PRECISION;
+          state = kPrecision;
         } else {
-          state = SPECIFIER;
+          state = kSpecifier;
           continue;  // Re-process same character.
         }
         break;
-      case PRECISION:
+      case kPrecision:
         if (c == L'*')
-          return -1;
+          return DataType::kInvalid;
         if (FXSYS_IsDecimalDigit(c)) {
           // Stay in same state.
           ++precision_digits;
         } else {
-          state = SPECIFIER;
+          state = kSpecifier;
           continue;  // Re-process same character.
         }
         break;
-      case SPECIFIER:
+      case kSpecifier:
         if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
             c == L'u' || c == L'x' || c == L'X') {
-          result = UTIL_INT;
+          result = DataType::kInt;
         } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' ||
                    c == L'G') {
-          result = UTIL_DOUBLE;
+          result = DataType::kDouble;
         } else if (c == L's' || c == L'S') {
           // Map s to S since we always deal internally with wchar_t strings.
           // TODO(tsepez): Probably 100% borked. %S is not a standard
           // conversion.
           sFormat->SetAt(i, L'S');
-          result = UTIL_STRING;
+          result = DataType::kString;
         } else {
-          return -1;
+          return DataType::kInvalid;
         }
-        state = AFTER;
+        state = kAfter;
         break;
-      case AFTER:
+      case kAfter:
         if (c == L'%')
-          return -1;
+          return DataType::kInvalid;
         // Stay in same state until string exhausted.
         break;
     }
     ++i;
   }
   // See https://crbug.com/740166
-  if (result == UTIL_INT && precision_digits > 2)
-    return -1;
+  if (result == DataType::kInt && precision_digits > 2)
+    return DataType::kInvalid;
 
   return result;
 }
diff --git a/fxjs/cjs_util.h b/fxjs/cjs_util.h
index 6f55b3a..45e58e0 100644
--- a/fxjs/cjs_util.h
+++ b/fxjs/cjs_util.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,15 +12,18 @@
 #include "core/fxcrt/widestring.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/js_define.h"
-
-// Return values for ParseDataType() below.
-#define UTIL_INT 0
-#define UTIL_DOUBLE 1
-#define UTIL_STRING 2
+#include "v8/include/v8-forward.h"
 
 class CJS_Util final : public CJS_Object {
  public:
-  static int GetObjDefnID();
+  enum class DataType {
+    kInvalid = -1,
+    kInt = 0,
+    kDouble = 1,
+    kString = 2,
+  };
+
+  static uint32_t GetObjDefnID();
   static void DefineJSObjects(CFXJS_Engine* pEngine);
 
   CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime);
@@ -33,7 +36,7 @@
   // byte-by-byte.
   //
   // Exposed for testing.
-  static int ParseDataType(WideString* sFormat);
+  static DataType ParseDataType(WideString* sFormat);
 
   // Exposed for testing.
   static WideString StringPrintx(const WideString& cFormat,
@@ -46,7 +49,7 @@
   JS_STATIC_METHOD(byteToChar, CJS_Util)
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const char kName[];
   static const JSMethodSpec MethodSpecs[];
 
diff --git a/fxjs/cjs_util_unittest.cpp b/fxjs/cjs_util_unittest.cpp
index d405746..b925268 100644
--- a/fxjs/cjs_util_unittest.cpp
+++ b/fxjs/cjs_util_unittest.cpp
@@ -1,110 +1,112 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/cjs_util.h"
 
+#include <iterator>
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(CJS_Util, ParseDataType) {
   struct ParseDataTypeCase {
     const wchar_t* const input_string;
-    const int expected;
+    const CJS_Util::DataType expected;
   };
 
   // Commented out tests follow the spec but are not passing.
   const ParseDataTypeCase cases[] = {
       // Not conversions
-      {L"", -1},
-      {L"d", -1},
+      {L"", CJS_Util::DataType::kInvalid},
+      {L"d", CJS_Util::DataType::kInvalid},
 
       // Simple cases
-      {L"%d", UTIL_INT},
-      {L"%x", UTIL_INT},
-      {L"%f", UTIL_DOUBLE},
-      {L"%s", UTIL_STRING},
+      {L"%d", CJS_Util::DataType::kInt},
+      {L"%x", CJS_Util::DataType::kInt},
+      {L"%f", CJS_Util::DataType::kDouble},
+      {L"%s", CJS_Util::DataType::kString},
 
       // nDecSep Not implemented
-      // {L"%,0d", UTIL_INT},
-      // {L"%,1d", UTIL_INT},
-      // {L"%,2d", UTIL_INT},
-      // {L"%,3d", UTIL_INT},
+      // {L"%,0d", CJS_Util::DataType::kInt},
+      // {L"%,1d", CJS_Util::DataType::kInt},
+      // {L"%,2d", CJS_Util::DataType::kInt},
+      // {L"%,3d", CJS_Util::DataType::kInt},
       // {L"%,4d", -1},
       // {L"%,d", -1},
 
       // cFlags("+ 0#"") are only valid for numeric conversions.
-      {L"%+d", UTIL_INT},
-      {L"%+x", UTIL_INT},
-      {L"%+f", UTIL_DOUBLE},
+      {L"%+d", CJS_Util::DataType::kInt},
+      {L"%+x", CJS_Util::DataType::kInt},
+      {L"%+f", CJS_Util::DataType::kDouble},
       // {L"%+s", -1},
-      {L"% d", UTIL_INT},
-      {L"% x", UTIL_INT},
-      {L"% f", UTIL_DOUBLE},
+      {L"% d", CJS_Util::DataType::kInt},
+      {L"% x", CJS_Util::DataType::kInt},
+      {L"% f", CJS_Util::DataType::kDouble},
       // {L"% s", -1},
-      {L"%0d", UTIL_INT},
-      {L"%0x", UTIL_INT},
-      {L"%0f", UTIL_DOUBLE},
+      {L"%0d", CJS_Util::DataType::kInt},
+      {L"%0x", CJS_Util::DataType::kInt},
+      {L"%0f", CJS_Util::DataType::kDouble},
       // {L"%0s", -1},
-      {L"%#d", UTIL_INT},
-      {L"%#x", UTIL_INT},
-      {L"%#f", UTIL_DOUBLE},
+      {L"%#d", CJS_Util::DataType::kInt},
+      {L"%#x", CJS_Util::DataType::kInt},
+      {L"%#f", CJS_Util::DataType::kDouble},
       // {L"%#s", -1},
 
       // nWidth should work. for all conversions, can be combined with cFlags=0
       // for numbers.
-      {L"%5d", UTIL_INT},
-      {L"%05d", UTIL_INT},
-      {L"%5x", UTIL_INT},
-      {L"%05x", UTIL_INT},
-      {L"%5f", UTIL_DOUBLE},
-      {L"%05f", UTIL_DOUBLE},
-      {L"%5s", UTIL_STRING},
+      {L"%5d", CJS_Util::DataType::kInt},
+      {L"%05d", CJS_Util::DataType::kInt},
+      {L"%5x", CJS_Util::DataType::kInt},
+      {L"%05x", CJS_Util::DataType::kInt},
+      {L"%5f", CJS_Util::DataType::kDouble},
+      {L"%05f", CJS_Util::DataType::kDouble},
+      {L"%5s", CJS_Util::DataType::kString},
       // {L"%05s", -1},
 
       // nPrecision should only work for float
       // {L"%.5d", -1},
       // {L"%.5x", -1},
-      {L"%.5f", UTIL_DOUBLE},
+      {L"%.5f", CJS_Util::DataType::kDouble},
       // {L"%.5s", -1},
       // {L"%.14d", -1},
       // {L"%.14x", -1},
-      {L"%.14f", UTIL_DOUBLE},
+      {L"%.14f", CJS_Util::DataType::kDouble},
       // {L"%.14s", -1},
       // {L"%.f", -1},
 
       // See https://crbug.com/740166
       // nPrecision too large (> 260) causes crashes in Windows.
       // Avoid this by limiting to two digits
-      {L"%.1d", UTIL_INT},
-      {L"%.10d", UTIL_INT},
-      {L"%.100d", -1},
+      {L"%.1d", CJS_Util::DataType::kInt},
+      {L"%.10d", CJS_Util::DataType::kInt},
+      {L"%.100d", CJS_Util::DataType::kInvalid},
 
       // Unexpected characters
-      {L"%ad", -1},
-      {L"%bx", -1},
-      // {L"%cf", -1},
-      // {L"%es", -1},
-      // {L"%gd", -1},
-      {L"%hx", -1},
-      // {L"%if", -1},
-      {L"%js", -1},
-      {L"%@d", -1},
-      {L"%~x", -1},
-      {L"%[f", -1},
-      {L"%\0s", -1},
-      {L"%\nd", -1},
-      {L"%\rx", -1},
-      // {L"%%f", -1},
-      // {L"%  s", -1},
+      {L"%ad", CJS_Util::DataType::kInvalid},
+      {L"%bx", CJS_Util::DataType::kInvalid},
+      // {L"%cf", CJS_Util::DataType::kInvalid},
+      // {L"%es", CJS_Util::DataType::kInvalid},
+      // {L"%gd", CJS_Util::DataType::kInvalid},
+      {L"%hx", CJS_Util::DataType::kInvalid},
+      // {L"%if", CJS_Util::DataType::kInvalid},
+      {L"%js", CJS_Util::DataType::kInvalid},
+      {L"%@d", CJS_Util::DataType::kInvalid},
+      {L"%~x", CJS_Util::DataType::kInvalid},
+      {L"%[f", CJS_Util::DataType::kInvalid},
+      {L"%\0s", CJS_Util::DataType::kInvalid},
+      {L"%\nd", CJS_Util::DataType::kInvalid},
+      {L"%\rx", CJS_Util::DataType::kInvalid},
+      // {L"%%f", CJS_Util::DataType::kInvalid},
+      // {L"%  s", CJS_Util::DataType::kInvalid},
 
       // Combine multiple valid components
-      {L"%+6d", UTIL_INT},
-      {L"% 7x", UTIL_INT},
-      {L"%#9.3f", UTIL_DOUBLE},
-      {L"%10s", UTIL_STRING},
+      {L"%+6d", CJS_Util::DataType::kInt},
+      {L"% 7x", CJS_Util::DataType::kInt},
+      {L"%#9.3f", CJS_Util::DataType::kDouble},
+      {L"%10s", CJS_Util::DataType::kString},
   };
 
-  for (size_t i = 0; i < FX_ArraySize(cases); i++) {
+  for (size_t i = 0; i < std::size(cases); i++) {
     WideString input(cases[i].input_string);
     EXPECT_EQ(cases[i].expected, CJS_Util::ParseDataType(&input))
         << cases[i].input_string;
diff --git a/fxjs/cjs_zoomtype.cpp b/fxjs/cjs_zoomtype.cpp
index cdaa2d5..b978468 100644
--- a/fxjs/cjs_zoomtype.cpp
+++ b/fxjs/cjs_zoomtype.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,7 @@
     {"pref", JSConstSpec::String, 0, "Preferred"},
     {"refW", JSConstSpec::String, 0, "ReflowWidth"}};
 
-int CJS_Zoomtype::ObjDefnID = -1;
+uint32_t CJS_Zoomtype::ObjDefnID = 0;
 
 // static
 void CJS_Zoomtype::DefineJSObjects(CFXJS_Engine* pEngine) {
diff --git a/fxjs/cjs_zoomtype.h b/fxjs/cjs_zoomtype.h
index de268cd..2efd49a 100644
--- a/fxjs/cjs_zoomtype.h
+++ b/fxjs/cjs_zoomtype.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
   CJS_Zoomtype() = delete;
 
  private:
-  static int ObjDefnID;
+  static uint32_t ObjDefnID;
   static const JSConstSpec ConstSpecs[];
 };
 
diff --git a/fxjs/fx_date_helpers.cpp b/fxjs/fx_date_helpers.cpp
index 5256fb1..3d7d1f3 100644
--- a/fxjs/fx_date_helpers.cpp
+++ b/fxjs/fx_date_helpers.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,11 @@
 
 #include "fxjs/fx_date_helpers.h"
 
+#include <math.h>
 #include <time.h>
+#include <wctype.h>
 
-#include <cmath>
+#include <iterator>
 
 #include "build/build_config.h"
 #include "core/fxcrt/fx_extension.h"
@@ -36,7 +38,7 @@
   time_t t = 0;
   FXSYS_time(&t);
   FXSYS_localtime(&t);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // In gcc 'timezone' is a global variable declared in time.h. In VC++, that
   // variable was removed in VC++ 2015, with _get_timezone replacing it.
   long timezone = 0;
@@ -113,9 +115,9 @@
   // Check for February onwards.
   static constexpr int kCumulativeDaysInMonths[] = {
       59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
-  for (size_t i = 0; i < FX_ArraySize(kCumulativeDaysInMonths); ++i) {
+  for (size_t i = 0; i < std::size(kCumulativeDaysInMonths); ++i) {
     if (day < kCumulativeDaysInMonths[i])
-      return i + 1;
+      return static_cast<int>(i) + 1;
   }
 
   return -1;
@@ -159,7 +161,7 @@
 size_t FindSubWordLength(const WideString& str, size_t nStart) {
   pdfium::span<const wchar_t> data = str.span();
   size_t i = nStart;
-  while (i < data.size() && std::iswalnum(data[i]))
+  while (i < data.size() && iswalnum(data[i]))
     ++i;
   return i - nStart;
 }
@@ -247,7 +249,7 @@
   double mn = Mod(m, 12);
   double t = TimeFromYearMonth(static_cast<int>(ym), static_cast<int>(mn));
   if (YearFromTime(t) != ym || MonthFromTime(t) != mn || DateFromTime(t) != 1)
-    return std::nan("");
+    return nan("");
 
   return Day(t) + dt - 1;
 }
@@ -261,8 +263,8 @@
 }
 
 double FX_MakeDate(double day, double time) {
-  if (!std::isfinite(day) || !std::isfinite(time))
-    return std::nan("");
+  if (!isfinite(day) || !isfinite(time))
+    return nan("");
 
   return day * 86400000 + time;
 }
@@ -433,9 +435,9 @@
               nSkip = FindSubWordLength(value, j);
               if (nSkip == KMonthAbbreviationLength) {
                 WideString sMonth = value.Substr(j, KMonthAbbreviationLength);
-                for (size_t m = 0; m < FX_ArraySize(kMonths); ++m) {
+                for (size_t m = 0; m < std::size(kMonths); ++m) {
                   if (sMonth.CompareNoCase(kMonths[m]) == 0) {
-                    nMonth = m + 1;
+                    nMonth = static_cast<int>(m) + 1;
                     i += 3;
                     j += nSkip;
                     bFind = true;
@@ -470,11 +472,11 @@
               if (nSkip <= kLongestFullMonthLength) {
                 WideString sMonth = value.Substr(j, nSkip);
                 sMonth.MakeLower();
-                for (size_t m = 0; m < FX_ArraySize(kFullMonths); ++m) {
+                for (size_t m = 0; m < std::size(kFullMonths); ++m) {
                   WideString sFullMonths = WideString(kFullMonths[m]);
                   sFullMonths.MakeLower();
-                  if (sFullMonths.Contains(sMonth.c_str())) {
-                    nMonth = m + 1;
+                  if (sFullMonths.Contains(sMonth.AsStringView())) {
+                    nMonth = static_cast<int>(m) + 1;
                     i += 4;
                     j += nSkip;
                     bFind = true;
@@ -482,7 +484,6 @@
                   }
                 }
               }
-
               if (!bFind) {
                 nMonth = FX_ParseStringInteger(value, j, &nSkip, 4);
                 i += 4;
@@ -541,7 +542,7 @@
 
   dt = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
                    FX_MakeTime(nHour, nMin, nSec, 0));
-  if (std::isnan(dt))
+  if (isnan(dt))
     return ConversionStatus::kBadDate;
 
   *result = dt;
diff --git a/fxjs/fx_date_helpers.h b/fxjs/fx_date_helpers.h
index 3657694..6f6f403 100644
--- a/fxjs/fx_date_helpers.h
+++ b/fxjs/fx_date_helpers.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 
 #include <stddef.h>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/widestring.h"
 
 namespace fxjs {
 
diff --git a/fxjs/fx_date_helpers_unittest.cpp b/fxjs/fx_date_helpers_unittest.cpp
index fb756b9..cc95719 100644
--- a/fxjs/fx_date_helpers_unittest.cpp
+++ b/fxjs/fx_date_helpers_unittest.cpp
@@ -1,9 +1,10 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/fx_date_helpers.h"
 
+#include "core/fxcrt/fake_time_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -12,6 +13,8 @@
 
 }  // namespace
 
+using fxjs::ConversionStatus;
+
 TEST(FX_DateHelper, GetYearFromTime) {
   static constexpr struct {
     double time_ms;
@@ -106,3 +109,47 @@
         << test.time_ms;
   }
 }
+
+using FXDateHelperFakeTimeTest = FakeTimeTest;
+
+TEST_F(FXDateHelperFakeTimeTest, ParseDateUsingFormatWithEmptyParams) {
+  double result = 0.0;
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"", L"", &result));
+  EXPECT_DOUBLE_EQ(1'587'654'321'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"value", L"", &result));
+  EXPECT_DOUBLE_EQ(1'587'654'321'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"", L"format", &result));
+  EXPECT_DOUBLE_EQ(1'587'654'321'000, result);
+}
+
+TEST_F(FXDateHelperFakeTimeTest, ParseDateUsingFormatForValidMonthDay) {
+  double result = 0.0;
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"01/02/2000", L"mm/dd/yyyy", &result));
+  EXPECT_DOUBLE_EQ(946'825'521'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"1/2/2000", L"m/d/yyyy", &result));
+  EXPECT_DOUBLE_EQ(946'825'521'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"1-2-2000", L"m-d-yyyy", &result));
+  EXPECT_DOUBLE_EQ(946'825'521'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"2-1-2000", L"d-m-yyyy", &result));
+  EXPECT_DOUBLE_EQ(946'825'521'000, result);
+
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"11/12/2000", L"mm/dd/yyyy", &result));
+  EXPECT_DOUBLE_EQ(973'955'121'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"11/12/2000", L"m/d/yyyy", &result));
+  EXPECT_DOUBLE_EQ(973'955'121'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"11-12-2000", L"m-d-yyyy", &result));
+  EXPECT_DOUBLE_EQ(973'955'121'000, result);
+  EXPECT_EQ(ConversionStatus::kSuccess,
+            FX_ParseDateUsingFormat(L"12-11-2000", L"d-m-yyyy", &result));
+  EXPECT_DOUBLE_EQ(973'955'121'000, result);
+}
diff --git a/fxjs/fxv8.cpp b/fxjs/fxv8.cpp
new file mode 100644
index 0000000..bbf1ec6
--- /dev/null
+++ b/fxjs/fxv8.cpp
@@ -0,0 +1,337 @@
+// Copyright 2020 The PDFium Authors
+// 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
+
+#include "fxjs/fxv8.h"
+
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-date.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-value.h"
+
+namespace fxv8 {
+
+bool IsUndefined(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsUndefined();
+}
+
+bool IsNull(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsNull();
+}
+
+bool IsBoolean(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsBoolean();
+}
+
+bool IsString(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsString();
+}
+
+bool IsNumber(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsNumber();
+}
+
+bool IsInteger(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsInt32();
+}
+
+bool IsObject(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsObject();
+}
+
+bool IsArray(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsArray();
+}
+
+bool IsDate(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsDate();
+}
+
+bool IsFunction(v8::Local<v8::Value> value) {
+  return !value.IsEmpty() && value->IsFunction();
+}
+
+v8::Local<v8::Value> NewNullHelper(v8::Isolate* pIsolate) {
+  return v8::Null(pIsolate);
+}
+
+v8::Local<v8::Value> NewUndefinedHelper(v8::Isolate* pIsolate) {
+  return v8::Undefined(pIsolate);
+}
+
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, int number) {
+  return v8::Int32::New(pIsolate, number);
+}
+
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, double number) {
+  return v8::Number::New(pIsolate, number);
+}
+
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, float number) {
+  return v8::Number::New(pIsolate, number);
+}
+
+v8::Local<v8::Boolean> NewBooleanHelper(v8::Isolate* pIsolate, bool b) {
+  return v8::Boolean::New(pIsolate, b);
+}
+
+v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
+                                      ByteStringView str) {
+  return v8::String::NewFromUtf8(
+             pIsolate, str.unterminated_c_str(), v8::NewStringType::kNormal,
+             pdfium::base::checked_cast<int>(str.GetLength()))
+      .ToLocalChecked();
+}
+
+v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
+                                      WideStringView str) {
+  return NewStringHelper(pIsolate, FX_UTF8Encode(str).AsStringView());
+}
+
+v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate) {
+  return v8::Array::New(pIsolate);
+}
+
+v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate,
+                                    pdfium::span<v8::Local<v8::Value>> values) {
+  v8::Local<v8::Array> result = NewArrayHelper(pIsolate);
+  for (size_t i = 0; i < values.size(); ++i) {
+    fxv8::ReentrantPutArrayElementHelper(
+        pIsolate, result, i,
+        values[i].IsEmpty() ? fxv8::NewUndefinedHelper(pIsolate) : values[i]);
+  }
+  return result;
+}
+
+v8::Local<v8::Object> NewObjectHelper(v8::Isolate* pIsolate) {
+  return v8::Object::New(pIsolate);
+}
+
+v8::Local<v8::Date> NewDateHelper(v8::Isolate* pIsolate, double d) {
+  return v8::Date::New(pIsolate->GetCurrentContext(), d)
+      .ToLocalChecked()
+      .As<v8::Date>();
+}
+
+WideString ToWideString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue) {
+  v8::String::Utf8Value s(pIsolate, pValue);
+  return WideString::FromUTF8(ByteStringView(*s, s.length()));
+}
+
+ByteString ToByteString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue) {
+  v8::String::Utf8Value s(pIsolate, pValue);
+  return ByteString(*s, s.length());
+}
+
+int ReentrantToInt32Helper(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return 0;
+  v8::TryCatch squash_exceptions(pIsolate);
+  return pValue->Int32Value(pIsolate->GetCurrentContext()).FromMaybe(0);
+}
+
+bool ReentrantToBooleanHelper(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return false;
+  v8::TryCatch squash_exceptions(pIsolate);
+  return pValue->BooleanValue(pIsolate);
+}
+
+float ReentrantToFloatHelper(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue) {
+  return static_cast<float>(ReentrantToDoubleHelper(pIsolate, pValue));
+}
+
+double ReentrantToDoubleHelper(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return 0.0;
+  v8::TryCatch squash_exceptions(pIsolate);
+  return pValue->NumberValue(pIsolate->GetCurrentContext()).FromMaybe(0.0);
+}
+
+WideString ReentrantToWideStringHelper(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return WideString();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::MaybeLocal<v8::String> maybe_string =
+      pValue->ToString(pIsolate->GetCurrentContext());
+  if (maybe_string.IsEmpty())
+    return WideString();
+
+  return ToWideString(pIsolate, maybe_string.ToLocalChecked());
+}
+
+ByteString ReentrantToByteStringHelper(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return ByteString();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::MaybeLocal<v8::String> maybe_string =
+      pValue->ToString(pIsolate->GetCurrentContext());
+  if (maybe_string.IsEmpty())
+    return ByteString();
+
+  return ToByteString(pIsolate, maybe_string.ToLocalChecked());
+}
+
+v8::Local<v8::Object> ReentrantToObjectHelper(v8::Isolate* pIsolate,
+                                              v8::Local<v8::Value> pValue) {
+  if (!fxv8::IsObject(pValue))
+    return v8::Local<v8::Object>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
+  return pValue->ToObject(context).ToLocalChecked();
+}
+
+v8::Local<v8::Array> ReentrantToArrayHelper(v8::Isolate* pIsolate,
+                                            v8::Local<v8::Value> pValue) {
+  if (!fxv8::IsArray(pValue))
+    return v8::Local<v8::Array>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
+  return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
+}
+
+v8::Local<v8::Value> ReentrantGetObjectPropertyHelper(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObj,
+    ByteStringView bsUTF8PropertyName) {
+  if (pObj.IsEmpty())
+    return v8::Local<v8::Value>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Value> val;
+  if (!pObj->Get(pIsolate->GetCurrentContext(),
+                 NewStringHelper(pIsolate, bsUTF8PropertyName))
+           .ToLocal(&val)) {
+    return v8::Local<v8::Value>();
+  }
+  return val;
+}
+
+std::vector<WideString> ReentrantGetObjectPropertyNamesHelper(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObj) {
+  if (pObj.IsEmpty())
+    return std::vector<WideString>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Array> val;
+  v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
+  if (!pObj->GetPropertyNames(context).ToLocal(&val))
+    return std::vector<WideString>();
+
+  std::vector<WideString> result;
+  for (uint32_t i = 0; i < val->Length(); ++i) {
+    result.push_back(ReentrantToWideStringHelper(
+        pIsolate, val->Get(context, i).ToLocalChecked()));
+  }
+  return result;
+}
+
+bool ReentrantHasObjectOwnPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName) {
+  if (pObj.IsEmpty())
+    return false;
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Context> pContext = pIsolate->GetCurrentContext();
+  v8::Local<v8::String> hKey =
+      fxv8::NewStringHelper(pIsolate, bsUTF8PropertyName);
+  return pObj->HasRealNamedProperty(pContext, hKey).FromJust();
+}
+
+bool ReentrantSetObjectOwnPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName,
+                                         v8::Local<v8::Value> pValue) {
+  if (pObj.IsEmpty() || pValue.IsEmpty())
+    return false;
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::String> name = NewStringHelper(pIsolate, bsUTF8PropertyName);
+  return pObj->DefineOwnProperty(pIsolate->GetCurrentContext(), name, pValue)
+      .FromMaybe(false);
+}
+
+bool ReentrantPutObjectPropertyHelper(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Object> pObj,
+                                      ByteStringView bsUTF8PropertyName,
+                                      v8::Local<v8::Value> pPut) {
+  if (pObj.IsEmpty() || pPut.IsEmpty())
+    return false;
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::String> name = NewStringHelper(pIsolate, bsUTF8PropertyName);
+  v8::Maybe<bool> result = pObj->Set(pIsolate->GetCurrentContext(), name, pPut);
+  return result.IsJust() && result.FromJust();
+}
+
+void ReentrantDeleteObjectPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName) {
+  v8::TryCatch squash_exceptions(pIsolate);
+  pObj->Delete(pIsolate->GetCurrentContext(),
+               fxv8::NewStringHelper(pIsolate, bsUTF8PropertyName))
+      .FromJust();
+}
+
+bool ReentrantPutArrayElementHelper(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Array> pArray,
+                                    size_t index,
+                                    v8::Local<v8::Value> pValue) {
+  if (pArray.IsEmpty())
+    return false;
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Maybe<bool> result =
+      pArray->Set(pIsolate->GetCurrentContext(),
+                  pdfium::base::checked_cast<uint32_t>(index), pValue);
+  return result.IsJust() && result.FromJust();
+}
+
+v8::Local<v8::Value> ReentrantGetArrayElementHelper(v8::Isolate* pIsolate,
+                                                    v8::Local<v8::Array> pArray,
+                                                    size_t index) {
+  if (pArray.IsEmpty())
+    return v8::Local<v8::Value>();
+
+  v8::TryCatch squash_exceptions(pIsolate);
+  v8::Local<v8::Value> val;
+  if (!pArray
+           ->Get(pIsolate->GetCurrentContext(),
+                 pdfium::base::checked_cast<uint32_t>(index))
+           .ToLocal(&val)) {
+    return v8::Local<v8::Value>();
+  }
+  return val;
+}
+
+size_t GetArrayLengthHelper(v8::Local<v8::Array> pArray) {
+  if (pArray.IsEmpty())
+    return 0;
+  return pArray->Length();
+}
+
+void ThrowExceptionHelper(v8::Isolate* pIsolate, ByteStringView str) {
+  pIsolate->ThrowException(NewStringHelper(pIsolate, str));
+}
+
+void ThrowExceptionHelper(v8::Isolate* pIsolate, WideStringView str) {
+  pIsolate->ThrowException(NewStringHelper(pIsolate, str));
+}
+
+}  // namespace fxv8
diff --git a/fxjs/fxv8.h b/fxjs/fxv8.h
new file mode 100644
index 0000000..5ebd7f5
--- /dev/null
+++ b/fxjs/fxv8.h
@@ -0,0 +1,110 @@
+// Copyright 2020 The PDFium Authors
+// 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 FXJS_FXV8_H_
+#define FXJS_FXV8_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "core/fxcrt/fx_string.h"
+#include "third_party/base/span.h"
+#include "v8/include/v8-forward.h"
+
+// The fxv8 functions soften up the interface to the V8 API. In particular,
+// PDFium uses size_t for sizes and indices, but V8 mostly uses ints, so
+// these routines perform checked conversions.
+
+namespace fxv8 {
+
+// These first check for empty locals.
+bool IsUndefined(v8::Local<v8::Value> value);
+bool IsNull(v8::Local<v8::Value> value);
+bool IsBoolean(v8::Local<v8::Value> value);
+bool IsString(v8::Local<v8::Value> value);
+bool IsNumber(v8::Local<v8::Value> value);
+bool IsInteger(v8::Local<v8::Value> value);
+bool IsObject(v8::Local<v8::Value> value);
+bool IsArray(v8::Local<v8::Value> value);
+bool IsDate(v8::Local<v8::Value> value);
+bool IsFunction(v8::Local<v8::Value> value);
+
+v8::Local<v8::Value> NewNullHelper(v8::Isolate* pIsolate);
+v8::Local<v8::Value> NewUndefinedHelper(v8::Isolate* pIsolate);
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, int number);
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, double number);
+v8::Local<v8::Number> NewNumberHelper(v8::Isolate* pIsolate, float number);
+v8::Local<v8::Boolean> NewBooleanHelper(v8::Isolate* pIsolate, bool b);
+v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
+                                      ByteStringView str);
+v8::Local<v8::String> NewStringHelper(v8::Isolate* pIsolate,
+                                      WideStringView str);
+v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate);
+v8::Local<v8::Array> NewArrayHelper(v8::Isolate* pIsolate,
+                                    pdfium::span<v8::Local<v8::Value>> values);
+v8::Local<v8::Object> NewObjectHelper(v8::Isolate* pIsolate);
+v8::Local<v8::Date> NewDateHelper(v8::Isolate* pIsolate, double d);
+
+// Conversion to PDFium type without re-entry from known v8 type.
+WideString ToWideString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue);
+ByteString ToByteString(v8::Isolate* pIsolate, v8::Local<v8::String> pValue);
+
+// Conversion to PDFium type with possible re-entry for coercion.
+int32_t ReentrantToInt32Helper(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> pValue);
+bool ReentrantToBooleanHelper(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value> pValue);
+float ReentrantToFloatHelper(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue);
+double ReentrantToDoubleHelper(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> pValue);
+WideString ReentrantToWideStringHelper(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue);
+ByteString ReentrantToByteStringHelper(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue);
+v8::Local<v8::Object> ReentrantToObjectHelper(v8::Isolate* pIsolate,
+                                              v8::Local<v8::Value> pValue);
+v8::Local<v8::Array> ReentrantToArrayHelper(v8::Isolate* pIsolate,
+                                            v8::Local<v8::Value> pValue);
+
+v8::Local<v8::Value> ReentrantGetObjectPropertyHelper(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObj,
+    ByteStringView bsUTF8PropertyName);
+std::vector<WideString> ReentrantGetObjectPropertyNamesHelper(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObj);
+bool ReentrantHasObjectOwnPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName);
+bool ReentrantSetObjectOwnPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName,
+                                         v8::Local<v8::Value> pValue);
+bool ReentrantPutObjectPropertyHelper(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Object> pObj,
+                                      ByteStringView bsUTF8PropertyName,
+                                      v8::Local<v8::Value> pPut);
+void ReentrantDeleteObjectPropertyHelper(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObj,
+                                         ByteStringView bsUTF8PropertyName);
+
+bool ReentrantPutArrayElementHelper(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Array> pArray,
+                                    size_t index,
+                                    v8::Local<v8::Value> pValue);
+v8::Local<v8::Value> ReentrantGetArrayElementHelper(v8::Isolate* pIsolate,
+                                                    v8::Local<v8::Array> pArray,
+                                                    size_t index);
+size_t GetArrayLengthHelper(v8::Local<v8::Array> pArray);
+
+void ThrowExceptionHelper(v8::Isolate* pIsolate, ByteStringView str);
+void ThrowExceptionHelper(v8::Isolate* pIsolate, WideStringView str);
+
+}  // namespace fxv8
+
+#endif  // FXJS_FXV8_H_
diff --git a/fxjs/gc/container_trace.h b/fxjs/gc/container_trace.h
new file mode 100644
index 0000000..dfe4a74
--- /dev/null
+++ b/fxjs/gc/container_trace.h
@@ -0,0 +1,66 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_CONTAINER_TRACE_H_
+#define FXJS_GC_CONTAINER_TRACE_H_
+
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+
+namespace fxgc {
+
+template <typename T, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor, const std::list<cppgc::Member<T>>& container) {
+  for (const auto& item : container)
+    visitor->Trace(item);
+}
+
+template <typename T, typename U, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor,
+                    const std::map<cppgc::Member<T>, U>& container) {
+  for (const auto& item : container) {
+    visitor->Trace(item.first);
+  }
+}
+
+template <typename T, typename U, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor,
+                    const std::map<U, cppgc::Member<T>>& container) {
+  for (const auto& item : container)
+    visitor->Trace(item.second);
+}
+
+template <typename T, typename U, typename V = cppgc::Visitor>
+void ContainerTrace(
+    V* visitor,
+    const std::map<cppgc::Member<U>, cppgc::Member<T>>& container) {
+  for (const auto& item : container) {
+    visitor->Trace(item.first);
+    visitor->Trace(item.second);
+  }
+}
+
+template <typename T, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor, const std::set<cppgc::Member<T>>& container) {
+  for (const auto& item : container)
+    visitor->Trace(item);
+}
+
+template <typename T, typename V = cppgc::Visitor>
+void ContainerTrace(V* visitor,
+                    const std::vector<cppgc::Member<T>>& container) {
+  for (const auto& item : container)
+    visitor->Trace(item);
+}
+
+}  // namespace fxgc
+
+using fxgc::ContainerTrace;
+
+#endif  // FXJS_GC_CONTAINER_TRACE_H_
diff --git a/fxjs/gc/container_trace_unittest.cpp b/fxjs/gc/container_trace_unittest.cpp
new file mode 100644
index 0000000..9273afe
--- /dev/null
+++ b/fxjs/gc/container_trace_unittest.cpp
@@ -0,0 +1,89 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/container_trace.h"
+
+#include <stdint.h>
+
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/cppgc/member.h"
+
+namespace {
+
+class Thing : public cppgc::GarbageCollected<Thing> {
+ public:
+  void Trace(cppgc::Visitor* visitor) const {}
+};
+
+class CountingVisitor {
+ public:
+  CountingVisitor() = default;
+
+  void Trace(const void* that) { ++call_count_; }
+  int call_count() const { return call_count_; }
+
+ private:
+  int call_count_ = 0;
+};
+
+}  // namespace
+
+TEST(ContainerTrace, ActualListTrace) {
+  std::list<cppgc::Member<Thing>> thing;
+  thing.emplace_back(nullptr);
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualMapTraceFirst) {
+  std::map<cppgc::Member<Thing>, int> thing;
+  thing[nullptr] = 42;
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualMapTraceSecond) {
+  std::map<int, cppgc::Member<Thing>> thing;
+  thing[42] = nullptr;
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualMapTraceBoth) {
+  std::map<cppgc::Member<Thing>, cppgc::Member<Thing>> thing;
+  thing[nullptr] = nullptr;
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(2, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualSetTrace) {
+  std::set<cppgc::Member<Thing>> thing;
+  thing.insert(nullptr);
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
+
+TEST(ContainerTrace, ActualVectorTrace) {
+  std::vector<cppgc::Member<Thing>> thing;
+  thing.emplace_back(nullptr);
+
+  CountingVisitor cv;
+  ContainerTrace(&cv, thing);
+  EXPECT_EQ(1, cv.call_count());
+}
diff --git a/fxjs/gc/gced_tree_node.h b/fxjs/gc/gced_tree_node.h
new file mode 100644
index 0000000..fed5e73
--- /dev/null
+++ b/fxjs/gc/gced_tree_node.h
@@ -0,0 +1,47 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_GCED_TREE_NODE_H_
+#define FXJS_GC_GCED_TREE_NODE_H_
+
+#include "core/fxcrt/tree_node.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+
+namespace fxjs {
+
+// For DOM/XML-ish trees, where references outside the tree are Persistent<>.
+template <typename T>
+class GCedTreeNode : public cppgc::GarbageCollected<GCedTreeNode<T>>,
+                     public fxcrt::TreeNodeBase<T> {
+ public:
+  virtual void Trace(cppgc::Visitor* visitor) const {
+    visitor->Trace(m_pParent);
+    visitor->Trace(m_pFirstChild);
+    visitor->Trace(m_pLastChild);
+    visitor->Trace(m_pNextSibling);
+    visitor->Trace(m_pPrevSibling);
+  }
+
+ protected:
+  GCedTreeNode() = default;
+  GCedTreeNode(const GCedTreeNode& that) = delete;
+  GCedTreeNode& operator=(const GCedTreeNode& that) = delete;
+
+ private:
+  friend class fxcrt::TreeNodeBase<T>;
+
+  cppgc::Member<T> m_pParent;
+  cppgc::Member<T> m_pFirstChild;
+  cppgc::Member<T> m_pLastChild;
+  cppgc::Member<T> m_pNextSibling;
+  cppgc::Member<T> m_pPrevSibling;
+};
+
+}  // namespace fxjs
+
+using fxjs::GCedTreeNode;
+
+#endif  // FXJS_GC_GCED_TREE_NODE_H_
diff --git a/fxjs/gc/gced_tree_node_mixin.h b/fxjs/gc/gced_tree_node_mixin.h
new file mode 100644
index 0000000..2f160e0
--- /dev/null
+++ b/fxjs/gc/gced_tree_node_mixin.h
@@ -0,0 +1,48 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_GCED_TREE_NODE_MIXIN_H_
+#define FXJS_GC_GCED_TREE_NODE_MIXIN_H_
+
+#include "core/fxcrt/tree_node.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/visitor.h"
+
+namespace fxjs {
+
+// For DOM/XML-ish trees, where references outside the tree are Persistent<>,
+// usable by classes that are already garbage collected themselves.
+template <typename T>
+class GCedTreeNodeMixin : public cppgc::GarbageCollectedMixin,
+                          public fxcrt::TreeNodeBase<T> {
+ public:
+  virtual void Trace(cppgc::Visitor* visitor) const {
+    visitor->Trace(m_pParent);
+    visitor->Trace(m_pFirstChild);
+    visitor->Trace(m_pLastChild);
+    visitor->Trace(m_pNextSibling);
+    visitor->Trace(m_pPrevSibling);
+  }
+
+ protected:
+  GCedTreeNodeMixin() = default;
+  GCedTreeNodeMixin(const GCedTreeNodeMixin& that) = delete;
+  GCedTreeNodeMixin& operator=(const GCedTreeNodeMixin& that) = delete;
+
+ private:
+  friend class fxcrt::TreeNodeBase<T>;
+
+  cppgc::Member<T> m_pParent;
+  cppgc::Member<T> m_pFirstChild;
+  cppgc::Member<T> m_pLastChild;
+  cppgc::Member<T> m_pNextSibling;
+  cppgc::Member<T> m_pPrevSibling;
+};
+
+}  // namespace fxjs
+
+using fxjs::GCedTreeNodeMixin;
+
+#endif  // FXJS_GC_GCED_TREE_NODE_MIXIN_H_
diff --git a/fxjs/gc/gced_tree_node_mixin_unittest.cpp b/fxjs/gc/gced_tree_node_mixin_unittest.cpp
new file mode 100644
index 0000000..2cd098e
--- /dev/null
+++ b/fxjs/gc/gced_tree_node_mixin_unittest.cpp
@@ -0,0 +1,149 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/gced_tree_node_mixin.h"
+
+#include <map>
+
+#include "core/fxcrt/observed_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/persistent.h"
+
+namespace {
+
+class ObservableGCedTreeNodeMixinForTest
+    : public cppgc::GarbageCollected<ObservableGCedTreeNodeMixinForTest>,
+      public GCedTreeNodeMixin<ObservableGCedTreeNodeMixinForTest>,
+      public Observable {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+
+  // GCedTreeNodeMixin:
+  void Trace(cppgc::Visitor* visitor) const override {
+    GCedTreeNodeMixin<ObservableGCedTreeNodeMixinForTest>::Trace(visitor);
+  }
+
+ private:
+  ObservableGCedTreeNodeMixinForTest() = default;
+};
+
+}  // namespace
+
+class GCedTreeNodeMixinUnitTest : public FXGCUnitTest {
+ public:
+  GCedTreeNodeMixinUnitTest() = default;
+  ~GCedTreeNodeMixinUnitTest() override = default;
+
+  // FXGCUnitTest:
+  void TearDown() override {
+    root_ = nullptr;  // Can't (yet) outlive |FXGCUnitTest::heap_|.
+    FXGCUnitTest::TearDown();
+  }
+
+  ObservableGCedTreeNodeMixinForTest* root() const { return root_; }
+  void CreateRoot() { root_ = CreateNode(); }
+
+  ObservableGCedTreeNodeMixinForTest* CreateNode() {
+    return cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
+        heap()->GetAllocationHandle());
+  }
+
+  void AddClutterToFront(ObservableGCedTreeNodeMixinForTest* parent) {
+    for (int i = 0; i < 4; ++i) {
+      parent->AppendFirstChild(
+          cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
+              heap()->GetAllocationHandle()));
+    }
+  }
+
+  void AddClutterToBack(ObservableGCedTreeNodeMixinForTest* parent) {
+    for (int i = 0; i < 4; ++i) {
+      parent->AppendLastChild(
+          cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
+              heap()->GetAllocationHandle()));
+    }
+  }
+
+ private:
+  cppgc::Persistent<ObservableGCedTreeNodeMixinForTest> root_;
+};
+
+TEST_F(GCedTreeNodeMixinUnitTest, OneRefence) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(root());
+  ForceGCAndPump();
+  EXPECT_TRUE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, NoReferences) {
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
+  ForceGCAndPump();
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, FirstHasParent) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_FALSE(watcher);
+
+  // Now add some clutter.
+  watcher.Reset(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  AddClutterToFront(root());
+  AddClutterToBack(root());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, RemoveSelf) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  ASSERT_TRUE(watcher);
+  watcher->RemoveSelfIfParented();
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, InsertBeforeAfter) {
+  CreateRoot();
+  AddClutterToFront(root());
+  ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  root()->InsertBefore(root()->GetFirstChild(), root()->GetLastChild());
+  root()->InsertAfter(root()->GetLastChild(), root()->GetFirstChild());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeMixinUnitTest, AsMapKey) {
+  std::map<cppgc::Persistent<ObservableGCedTreeNodeMixinForTest>, int> score;
+  ObservableGCedTreeNodeMixinForTest* node = CreateNode();
+  score[node] = 100;
+  EXPECT_EQ(100, score[node]);
+}
diff --git a/fxjs/gc/gced_tree_node_unittest.cpp b/fxjs/gc/gced_tree_node_unittest.cpp
new file mode 100644
index 0000000..a456a31
--- /dev/null
+++ b/fxjs/gc/gced_tree_node_unittest.cpp
@@ -0,0 +1,143 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/gced_tree_node.h"
+
+#include <map>
+
+#include "core/fxcrt/observed_ptr.h"
+#include "fxjs/gc/heap.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/persistent.h"
+
+namespace {
+
+class ObservableGCedTreeNodeForTest
+    : public GCedTreeNode<ObservableGCedTreeNodeForTest>,
+      public Observable {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+
+ private:
+  ObservableGCedTreeNodeForTest() = default;
+};
+
+}  // namespace
+
+class GCedTreeNodeUnitTest : public FXGCUnitTest {
+ public:
+  GCedTreeNodeUnitTest() = default;
+  ~GCedTreeNodeUnitTest() override = default;
+
+  // FXGCUnitTest:
+  void TearDown() override {
+    root_ = nullptr;  // Can't (yet) outlive |FXGCUnitTest::heap_|.
+    FXGCUnitTest::TearDown();
+  }
+
+  ObservableGCedTreeNodeForTest* root() const { return root_; }
+  void CreateRoot() { root_ = CreateNode(); }
+
+  ObservableGCedTreeNodeForTest* CreateNode() {
+    return cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
+        heap()->GetAllocationHandle());
+  }
+
+  void AddClutterToFront(ObservableGCedTreeNodeForTest* parent) {
+    for (int i = 0; i < 4; ++i) {
+      parent->AppendFirstChild(
+          cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
+              heap()->GetAllocationHandle()));
+    }
+  }
+
+  void AddClutterToBack(ObservableGCedTreeNodeForTest* parent) {
+    for (int i = 0; i < 4; ++i) {
+      parent->AppendLastChild(
+          cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
+              heap()->GetAllocationHandle()));
+    }
+  }
+
+ private:
+  cppgc::Persistent<ObservableGCedTreeNodeForTest> root_;
+};
+
+TEST_F(GCedTreeNodeUnitTest, OneRefence) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(root());
+  ForceGCAndPump();
+  EXPECT_TRUE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, NoReferences) {
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
+  ForceGCAndPump();
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, FirstHasParent) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_FALSE(watcher);
+
+  // Now add some clutter.
+  watcher.Reset(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  AddClutterToFront(root());
+  AddClutterToBack(root());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, RemoveSelf) {
+  CreateRoot();
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  ASSERT_TRUE(watcher);
+  watcher->RemoveSelfIfParented();
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, InsertBeforeAfter) {
+  CreateRoot();
+  AddClutterToFront(root());
+  ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
+  root()->AppendFirstChild(watcher.Get());
+  root()->InsertBefore(root()->GetFirstChild(), root()->GetLastChild());
+  root()->InsertAfter(root()->GetLastChild(), root()->GetFirstChild());
+  ForceGCAndPump();
+  ASSERT_TRUE(root());
+  EXPECT_TRUE(watcher);
+  root()->RemoveChild(watcher.Get());
+  ForceGCAndPump();
+  EXPECT_TRUE(root());
+  EXPECT_FALSE(watcher);
+}
+
+TEST_F(GCedTreeNodeUnitTest, AsMapKey) {
+  std::map<cppgc::Persistent<ObservableGCedTreeNodeForTest>, int> score;
+  ObservableGCedTreeNodeForTest* node = CreateNode();
+  score[node] = 100;
+  EXPECT_EQ(100, score[node]);
+}
diff --git a/fxjs/gc/heap.cpp b/fxjs/gc/heap.cpp
new file mode 100644
index 0000000..301018f
--- /dev/null
+++ b/fxjs/gc/heap.cpp
@@ -0,0 +1,98 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/heap.h"
+
+#include <utility>
+
+#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
+#include "v8/include/cppgc/heap.h"
+
+namespace {
+
+size_t g_platform_ref_count = 0;
+v8::Platform* g_platform = nullptr;
+v8::Isolate* g_isolate = nullptr;
+
+}  // namespace
+
+// Taken from v8/samples/cppgc/cppgc-for-v8-embedders.cc.
+// Adaptper that makes the global v8::Platform compatible with a
+// cppgc::Platform.
+class CFXGC_Platform final : public cppgc::Platform {
+ public:
+  CFXGC_Platform() = default;
+  ~CFXGC_Platform() override = default;
+
+  cppgc::PageAllocator* GetPageAllocator() override {
+    return g_platform->GetPageAllocator();
+  }
+
+  double MonotonicallyIncreasingTime() override {
+    return g_platform->MonotonicallyIncreasingTime();
+  }
+
+  std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() override {
+    // V8's default platform creates a new task runner when passed the
+    // v8::Isolate pointer the first time. For non-default platforms this will
+    // require getting the appropriate task runner.
+    return g_platform->GetForegroundTaskRunner(g_isolate);
+  }
+
+  std::unique_ptr<cppgc::JobHandle> PostJob(
+      cppgc::TaskPriority priority,
+      std::unique_ptr<cppgc::JobTask> job_task) override {
+    return g_platform->PostJob(priority, std::move(job_task));
+  }
+};
+
+void FXGC_Initialize(v8::Platform* platform, v8::Isolate* isolate) {
+  if (platform) {
+    DCHECK(!g_platform);
+    g_platform = platform;
+    g_isolate = isolate;
+  }
+}
+
+void FXGC_Release() {
+  if (g_platform && g_platform_ref_count == 0) {
+    g_platform = nullptr;
+    g_isolate = nullptr;
+  }
+}
+
+FXGCScopedHeap FXGC_CreateHeap() {
+  // If XFA is included at compile-time, but JS is disabled at run-time,
+  // we may still attempt to build a CPDFXFA_Context which will want a
+  // heap. But we can't make one because JS is disabled.
+  // TODO(tsepez): Stop the context from even being created.
+  if (!g_platform)
+    return nullptr;
+
+  ++g_platform_ref_count;
+  auto heap = cppgc::Heap::Create(
+      std::make_shared<CFXGC_Platform>(),
+      cppgc::Heap::HeapOptions{
+          {},
+          cppgc::Heap::StackSupport::kNoConservativeStackScan,
+          cppgc::Heap::MarkingType::kAtomic,
+          cppgc::Heap::SweepingType::kIncrementalAndConcurrent,
+          {}});
+  return FXGCScopedHeap(heap.release());
+}
+
+void FXGC_ForceGarbageCollection(cppgc::Heap* heap) {
+  heap->ForceGarbageCollectionSlow("FXGC", "ForceGarbageCollection",
+                                   cppgc::Heap::StackState::kNoHeapPointers);
+}
+
+void FXGCHeapDeleter::operator()(cppgc::Heap* heap) {
+  DCHECK(heap);
+  DCHECK(g_platform_ref_count > 0);
+  --g_platform_ref_count;
+
+  FXGC_ForceGarbageCollection(heap);
+  delete heap;
+}
diff --git a/fxjs/gc/heap.h b/fxjs/gc/heap.h
new file mode 100644
index 0000000..1e2bf61
--- /dev/null
+++ b/fxjs/gc/heap.h
@@ -0,0 +1,36 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_HEAP_H_
+#define FXJS_GC_HEAP_H_
+
+#include <memory>
+
+#include "v8/include/cppgc/allocation.h"
+
+namespace cppgc {
+class Heap;
+}  // namespace cppgc
+
+namespace v8 {
+class Isolate;
+class Platform;
+}  // namespace v8
+
+struct FXGCHeapDeleter {
+  void operator()(cppgc::Heap* heap);
+};
+
+using FXGCScopedHeap = std::unique_ptr<cppgc::Heap, FXGCHeapDeleter>;
+
+void FXGC_Initialize(v8::Platform* platform, v8::Isolate* isolate);
+void FXGC_Release();
+FXGCScopedHeap FXGC_CreateHeap();
+void FXGC_ForceGarbageCollection(cppgc::Heap* heap);
+
+#define CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED \
+  template <typename T>                      \
+  friend class cppgc::MakeGarbageCollectedTrait
+
+#endif  // FXJS_GC_HEAP_H_
diff --git a/fxjs/gc/heap_unittest.cpp b/fxjs/gc/heap_unittest.cpp
new file mode 100644
index 0000000..aa90d00
--- /dev/null
+++ b/fxjs/gc/heap_unittest.cpp
@@ -0,0 +1,175 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/heap.h"
+
+#include <memory>
+#include <set>
+
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/cppgc/persistent.h"
+
+namespace {
+
+class PseudoCollectible : public cppgc::GarbageCollected<PseudoCollectible> {
+ public:
+  static void ClearCounts() {
+    s_live_.clear();
+    s_dead_.clear();
+  }
+  static size_t LiveCount() { return s_live_.size(); }
+  static size_t DeadCount() { return s_dead_.size(); }
+
+  PseudoCollectible() { s_live_.insert(this); }
+  virtual ~PseudoCollectible() {
+    s_live_.erase(this);
+    s_dead_.insert(this);
+  }
+
+  bool IsLive() const { return pdfium::Contains(s_live_, this); }
+
+  virtual void Trace(cppgc::Visitor* visitor) const {}
+
+ private:
+  static std::set<const PseudoCollectible*> s_live_;
+  static std::set<const PseudoCollectible*> s_dead_;
+};
+
+std::set<const PseudoCollectible*> PseudoCollectible::s_live_;
+std::set<const PseudoCollectible*> PseudoCollectible::s_dead_;
+
+class CollectibleHolder {
+ public:
+  explicit CollectibleHolder(PseudoCollectible* holdee) : holdee_(holdee) {}
+  ~CollectibleHolder() = default;
+
+  PseudoCollectible* holdee() const { return holdee_; }
+
+ private:
+  cppgc::Persistent<PseudoCollectible> holdee_;
+};
+
+class Bloater : public cppgc::GarbageCollected<Bloater> {
+ public:
+  void Trace(cppgc::Visitor* visitor) const {}
+  uint8_t bloat_[65536];
+};
+
+}  // namespace
+
+class HeapUnitTest : public FXGCUnitTest {
+ public:
+  HeapUnitTest() = default;
+  ~HeapUnitTest() override = default;
+
+  // FXGCUnitTest:
+  void TearDown() override {
+    PseudoCollectible::ClearCounts();
+    FXGCUnitTest::TearDown();
+  }
+};
+
+TEST_F(HeapUnitTest, SeveralHeaps) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  EXPECT_TRUE(heap1);
+
+  FXGCScopedHeap heap2 = FXGC_CreateHeap();
+  EXPECT_TRUE(heap2);
+
+  FXGCScopedHeap heap3 = FXGC_CreateHeap();
+  EXPECT_TRUE(heap3);
+
+  // Test manually destroying the heap.
+  heap3.reset();
+  EXPECT_FALSE(heap3);
+  heap3.reset();
+  EXPECT_FALSE(heap3);
+}
+
+TEST_F(HeapUnitTest, NoReferences) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  ASSERT_TRUE(heap1);
+  {
+    auto holder = std::make_unique<CollectibleHolder>(
+        cppgc::MakeGarbageCollected<PseudoCollectible>(
+            heap1->GetAllocationHandle()));
+
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+  }
+  FXGC_ForceGarbageCollection(heap1.get());
+  EXPECT_EQ(0u, PseudoCollectible::LiveCount());
+  EXPECT_EQ(1u, PseudoCollectible::DeadCount());
+}
+
+TEST_F(HeapUnitTest, HasReferences) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  ASSERT_TRUE(heap1);
+  {
+    auto holder = std::make_unique<CollectibleHolder>(
+        cppgc::MakeGarbageCollected<PseudoCollectible>(
+            heap1->GetAllocationHandle()));
+
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+
+    FXGC_ForceGarbageCollection(heap1.get());
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+  }
+}
+
+// TODO(tsepez): enable when CPPGC fixes this segv.
+TEST_F(HeapUnitTest, DISABLED_DeleteHeapHasReferences) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  ASSERT_TRUE(heap1);
+  {
+    auto holder = std::make_unique<CollectibleHolder>(
+        cppgc::MakeGarbageCollected<PseudoCollectible>(
+            heap1->GetAllocationHandle()));
+
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+
+    heap1.reset();
+
+    // Maybe someday magically nulled by heap destruction.
+    EXPECT_FALSE(holder->holdee());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+  }
+}
+
+TEST_F(HeapUnitTest, DeleteHeapNoReferences) {
+  FXGCScopedHeap heap1 = FXGC_CreateHeap();
+  ASSERT_TRUE(heap1);
+  {
+    auto holder = std::make_unique<CollectibleHolder>(
+        cppgc::MakeGarbageCollected<PseudoCollectible>(
+            heap1->GetAllocationHandle()));
+
+    EXPECT_TRUE(holder->holdee()->IsLive());
+    EXPECT_EQ(1u, PseudoCollectible::LiveCount());
+    EXPECT_EQ(0u, PseudoCollectible::DeadCount());
+  }
+  heap1.reset();
+  EXPECT_EQ(0u, PseudoCollectible::LiveCount());
+  EXPECT_EQ(1u, PseudoCollectible::DeadCount());
+}
+
+TEST_F(HeapUnitTest, Bloat) {
+  ASSERT_TRUE(heap());
+  for (int i = 0; i < 100000; ++i) {
+    cppgc::MakeGarbageCollected<Bloater>(heap()->GetAllocationHandle());
+    Pump();  // Do not force GC, must happen implicitly when space required.
+  }
+}
diff --git a/fxjs/gc/move_unittest.cpp b/fxjs/gc/move_unittest.cpp
new file mode 100644
index 0000000..3d5a809
--- /dev/null
+++ b/fxjs/gc/move_unittest.cpp
@@ -0,0 +1,62 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "fxjs/gc/heap.h"
+#include "testing/fxgc_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/cppgc/persistent.h"
+
+namespace {
+
+class HeapObject : public cppgc::GarbageCollected<HeapObject> {
+ public:
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+
+  void Trace(cppgc::Visitor* visitor) const {
+    visitor->Trace(frick_);
+    visitor->Trace(frack_);
+  }
+
+  cppgc::Member<HeapObject> frick_;
+  cppgc::Member<HeapObject> frack_;
+
+ private:
+  HeapObject() = default;
+};
+
+class CppObject {
+ public:
+  CppObject() = default;
+
+  cppgc::Persistent<HeapObject> click_;
+  cppgc::Persistent<HeapObject> clack_;
+};
+
+}  // namespace
+
+class MoveUnitTest : public FXGCUnitTest {};
+
+TEST_F(MoveUnitTest, Member) {
+  // Moving a Member<> leaves the moved-from object as null.
+  auto* obj =
+      cppgc::MakeGarbageCollected<HeapObject>(heap()->GetAllocationHandle());
+  obj->frick_ = obj;
+  obj->frack_ = std::move(obj->frick_);
+  EXPECT_FALSE(obj->frick_);
+  EXPECT_EQ(obj, obj->frack_);
+}
+
+TEST_F(MoveUnitTest, Persistent) {
+  // Moving a Persistent<> leaves the moved-from object as null.
+  auto* obj =
+      cppgc::MakeGarbageCollected<HeapObject>(heap()->GetAllocationHandle());
+  CppObject outsider;
+  outsider.click_ = obj;
+  outsider.clack_ = std::move(outsider.click_);
+  EXPECT_FALSE(outsider.click_);
+  EXPECT_EQ(obj, outsider.clack_);
+}
diff --git a/fxjs/global_timer.cpp b/fxjs/global_timer.cpp
index b837508..035aff8 100644
--- a/fxjs/global_timer.cpp
+++ b/fxjs/global_timer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,10 @@
 
 #include <map>
 
-#include "core/fxcrt/timerhandler_iface.h"
+#include "core/fxcrt/cfx_timer.h"
 #include "fxjs/cjs_app.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 #include "third_party/base/no_destructor.h"
 
 namespace {
@@ -34,8 +36,10 @@
       m_swJScript(script),
       m_pRuntime(pRuntime),
       m_pEmbedApp(pObj) {
-  if (HasValidID())
+  if (HasValidID()) {
+    DCHECK(!pdfium::Contains(GetGlobalTimerMap(), m_nTimerID));
     GetGlobalTimerMap()[m_nTimerID] = this;
+  }
 }
 
 GlobalTimer::~GlobalTimer() {
@@ -45,6 +49,7 @@
   if (m_pRuntime && m_pRuntime->GetTimerHandler())
     m_pRuntime->GetTimerHandler()->KillTimer(m_nTimerID);
 
+  DCHECK(pdfium::Contains(GetGlobalTimerMap(), m_nTimerID));
   GetGlobalTimerMap().erase(m_nTimerID);
 }
 
@@ -84,5 +89,5 @@
 }
 
 bool GlobalTimer::HasValidID() const {
-  return m_nTimerID != TimerHandlerIface::kInvalidTimerID;
+  return m_nTimerID != CFX_Timer::HandlerIface::kInvalidTimerID;
 }
diff --git a/fxjs/global_timer.h b/fxjs/global_timer.h
index ef6bbb6..106c9e6 100644
--- a/fxjs/global_timer.h
+++ b/fxjs/global_timer.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef FXJS_GLOBAL_TIMER_H_
 #define FXJS_GLOBAL_TIMER_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cjs_runtime.h"
 
 class CJS_App;
@@ -44,7 +45,7 @@
   const uint32_t m_dwTimeOut;
   const WideString m_swJScript;
   ObservedPtr<CJS_Runtime> m_pRuntime;
-  CJS_App* const m_pEmbedApp;
+  UnownedPtr<CJS_App> const m_pEmbedApp;
 };
 
 #endif  // FXJS_GLOBAL_TIMER_H_
diff --git a/fxjs/ijs_event_context.h b/fxjs/ijs_event_context.h
index e2c6407..8e9d23c 100644
--- a/fxjs/ijs_event_context.h
+++ b/fxjs/ijs_event_context.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,15 +7,11 @@
 #ifndef FXJS_IJS_EVENT_CONTEXT_H_
 #define FXJS_IJS_EVENT_CONTEXT_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/widestring.h"
 #include "fxjs/ijs_runtime.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
-class CPDF_Bookmark;
 class CPDF_FormField;
-class CPDFSDK_Annot;
-class CPDFSDK_FormFillEnvironment;
 
 // Records the details of an event and triggers JS execution for it. There
 // can be more than one of these at any given time, as JS callbacks to C++
@@ -24,23 +20,20 @@
  public:
   virtual ~IJS_EventContext() = default;
 
-  virtual Optional<IJS_Runtime::JS_Error> RunScript(
+  virtual absl::optional<IJS_Runtime::JS_Error> RunScript(
       const WideString& script) = 0;
 
-  virtual void OnApp_Init() = 0;
+  virtual void OnDoc_Open(const WideString& strTargetName) = 0;
+  virtual void OnDoc_WillPrint() = 0;
+  virtual void OnDoc_DidPrint() = 0;
+  virtual void OnDoc_WillSave() = 0;
+  virtual void OnDoc_DidSave() = 0;
+  virtual void OnDoc_WillClose() = 0;
 
-  virtual void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                          const WideString& strTargetName) = 0;
-  virtual void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-
-  virtual void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
+  virtual void OnPage_Open() = 0;
+  virtual void OnPage_Close() = 0;
+  virtual void OnPage_InView() = 0;
+  virtual void OnPage_OutView() = 0;
 
   virtual void OnField_MouseDown(bool bModifier,
                                  bool bShift,
@@ -88,44 +81,6 @@
                                 WideString* Value,
                                 bool* bRc) = 0;
 
-  virtual void OnScreen_Focus(bool bModifier,
-                              bool bShift,
-                              CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_Blur(bool bModifier,
-                             bool bShift,
-                             CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_Open(bool bModifier,
-                             bool bShift,
-                             CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_Close(bool bModifier,
-                              bool bShift,
-                              CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_MouseDown(bool bModifier,
-                                  bool bShift,
-                                  CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_MouseUp(bool bModifier,
-                                bool bShift,
-                                CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_MouseEnter(bool bModifier,
-                                   bool bShift,
-                                   CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_MouseExit(bool bModifier,
-                                  bool bShift,
-                                  CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_InView(bool bModifier,
-                               bool bShift,
-                               CPDFSDK_Annot* pScreen) = 0;
-  virtual void OnScreen_OutView(bool bModifier,
-                                bool bShift,
-                                CPDFSDK_Annot* pScreen) = 0;
-
-  virtual void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) = 0;
-  virtual void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-
-  virtual void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv,
-                           const WideString&) = 0;
-  virtual void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0;
-  virtual void OnConsole_Exec() = 0;
   virtual void OnExternal_Exec() = 0;
 };
 
diff --git a/fxjs/ijs_runtime.cpp b/fxjs/ijs_runtime.cpp
index 34a846e..0308524 100644
--- a/fxjs/ijs_runtime.cpp
+++ b/fxjs/ijs_runtime.cpp
@@ -1,36 +1,46 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fxjs/ijs_runtime.h"
 
 #include "fxjs/cjs_runtimestub.h"
-#include "third_party/base/ptr_util.h"
 
 #ifdef PDF_ENABLE_V8
+#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fxjs/cfxjs_engine.h"
 #include "fxjs/cjs_runtime.h"
-#endif
+#ifdef PDF_ENABLE_XFA
+#include "fxjs/gc/heap.h"
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 
 IJS_Runtime::ScopedEventContext::ScopedEventContext(IJS_Runtime* pRuntime)
     : m_pRuntime(pRuntime), m_pContext(pRuntime->NewEventContext()) {}
 
 IJS_Runtime::ScopedEventContext::~ScopedEventContext() {
-  m_pRuntime->ReleaseEventContext(m_pContext.Release());
+  m_pRuntime->ReleaseEventContext(m_pContext.ExtractAsDangling());
 }
 
 // static
-void IJS_Runtime::Initialize(unsigned int slot, void* isolate) {
+void IJS_Runtime::Initialize(unsigned int slot, void* isolate, void* platform) {
 #ifdef PDF_ENABLE_V8
   FXJS_Initialize(slot, static_cast<v8::Isolate*>(isolate));
-#endif
+#ifdef PDF_ENABLE_XFA
+  FXGC_Initialize(static_cast<v8::Platform*>(platform),
+                  static_cast<v8::Isolate*>(isolate));
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 }
 
 // static
 void IJS_Runtime::Destroy() {
 #ifdef PDF_ENABLE_V8
+#ifdef PDF_ENABLE_XFA
+  FXGC_Release();
+#endif  // PDF_ENABLE_XFA
   FXJS_Release();
-#endif
+#endif  // PDF_ENABLE_V8
 }
 
 // static
@@ -38,9 +48,9 @@
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
 #ifdef PDF_ENABLE_V8
   if (pFormFillEnv->IsJSPlatformPresent())
-    return pdfium::MakeUnique<CJS_Runtime>(pFormFillEnv);
+    return std::make_unique<CJS_Runtime>(pFormFillEnv);
 #endif
-  return pdfium::MakeUnique<CJS_RuntimeStub>(pFormFillEnv);
+  return std::make_unique<CJS_RuntimeStub>(pFormFillEnv);
 }
 
 IJS_Runtime::~IJS_Runtime() = default;
diff --git a/fxjs/ijs_runtime.h b/fxjs/ijs_runtime.h
index ca103aa..d2ce19f 100644
--- a/fxjs/ijs_runtime.h
+++ b/fxjs/ijs_runtime.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,12 +9,11 @@
 
 #include <memory>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/optional.h"
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
-class CFXJSE_Value;
 class CJS_Runtime;
 class CPDFSDK_FormFillEnvironment;
 class IJS_EventContext;
@@ -34,18 +33,20 @@
 
   class ScopedEventContext {
    public:
+    FX_STACK_ALLOCATED();
+
     explicit ScopedEventContext(IJS_Runtime* pRuntime);
     ~ScopedEventContext();
 
-    IJS_EventContext* Get() const { return m_pContext.Get(); }
-    IJS_EventContext* operator->() const { return m_pContext.Get(); }
+    IJS_EventContext* Get() const { return m_pContext; }
+    IJS_EventContext* operator->() const { return m_pContext; }
 
    private:
     UnownedPtr<IJS_Runtime> const m_pRuntime;
     UnownedPtr<IJS_EventContext> m_pContext;
   };
 
-  static void Initialize(unsigned int slot, void* isolate);
+  static void Initialize(unsigned int slot, void* isolate, void* platform);
   static void Destroy();
   static std::unique_ptr<IJS_Runtime> Create(
       CPDFSDK_FormFillEnvironment* pFormFillEnv);
@@ -56,7 +57,7 @@
   virtual IJS_EventContext* NewEventContext() = 0;
   virtual void ReleaseEventContext(IJS_EventContext* pContext) = 0;
   virtual CPDFSDK_FormFillEnvironment* GetFormFillEnv() const = 0;
-  virtual Optional<JS_Error> ExecuteScript(const WideString& script) = 0;
+  virtual absl::optional<JS_Error> ExecuteScript(const WideString& script) = 0;
 
  protected:
   IJS_Runtime() = default;
diff --git a/fxjs/js_define.cpp b/fxjs/js_define.cpp
index 0c0c02c..b19c2e2 100644
--- a/fxjs/js_define.cpp
+++ b/fxjs/js_define.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,10 @@
 
 #include "fxjs/js_define.h"
 
+#include <math.h>
+#include <stdarg.h>
+
 #include <algorithm>
-#include <cmath>
 #include <limits>
 #include <vector>
 
@@ -15,50 +17,46 @@
 #include "fxjs/cjs_document.h"
 #include "fxjs/cjs_object.h"
 #include "fxjs/fx_date_helpers.h"
+#include "fxjs/fxv8.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-isolate.h"
 
 void JSDestructor(v8::Local<v8::Object> obj) {
   CFXJS_Engine::SetObjectPrivate(obj, nullptr);
 }
 
-double JS_DateParse(const WideString& str) {
-  v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
+double JS_DateParse(v8::Isolate* pIsolate, const WideString& str) {
   v8::Isolate::Scope isolate_scope(pIsolate);
   v8::HandleScope scope(pIsolate);
 
   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
 
   // Use the built-in object method.
-  v8::Local<v8::Value> v =
-      context->Global()
-          ->Get(context, v8::String::NewFromUtf8(pIsolate, "Date",
-                                                 v8::NewStringType::kNormal)
-                             .ToLocalChecked())
-          .ToLocalChecked();
-  if (v->IsObject()) {
-    v8::Local<v8::Object> o = v->ToObject(context).ToLocalChecked();
-    v = o->Get(context, v8::String::NewFromUtf8(pIsolate, "parse",
-                                                v8::NewStringType::kNormal)
-                            .ToLocalChecked())
-            .ToLocalChecked();
-    if (v->IsFunction()) {
-      v8::Local<v8::Function> funC = v8::Local<v8::Function>::Cast(v);
-      const int argc = 1;
-      v8::Local<v8::Value> timeStr =
-          v8::String::NewFromUtf8(pIsolate,
-                                  FX_UTF8Encode(str.AsStringView()).c_str(),
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked();
-      v8::Local<v8::Value> argv[argc] = {timeStr};
-      v = funC->Call(context, context->Global(), argc, argv).ToLocalChecked();
-      if (v->IsNumber()) {
-        double date = v->ToNumber(context).ToLocalChecked()->Value();
-        if (!std::isfinite(date))
-          return date;
-        return FX_LocalTime(date);
-      }
-    }
-  }
-  return 0;
+  v8::MaybeLocal<v8::Value> maybe_value =
+      context->Global()->Get(context, fxv8::NewStringHelper(pIsolate, "Date"));
+
+  v8::Local<v8::Value> value;
+  if (!maybe_value.ToLocal(&value) || !value->IsObject())
+    return 0;
+
+  v8::Local<v8::Object> obj = value.As<v8::Object>();
+  maybe_value = obj->Get(context, fxv8::NewStringHelper(pIsolate, "parse"));
+  if (!maybe_value.ToLocal(&value) || !value->IsFunction())
+    return 0;
+
+  v8::Local<v8::Function> func = value.As<v8::Function>();
+  static constexpr int argc = 1;
+  v8::Local<v8::Value> argv[argc] = {
+      fxv8::NewStringHelper(pIsolate, str.AsStringView()),
+  };
+  maybe_value = func->Call(context, context->Global(), argc, argv);
+  if (!maybe_value.ToLocal(&value) || !value->IsNumber())
+    return 0;
+
+  double date = value.As<v8::Number>()->Value();
+  return isfinite(date) ? FX_LocalTime(date) : date;
 }
 
 std::vector<v8::Local<v8::Value>> ExpandKeywordParams(
@@ -66,7 +64,7 @@
     const std::vector<v8::Local<v8::Value>>& originals,
     size_t nKeywords,
     ...) {
-  ASSERT(nKeywords);
+  DCHECK(nKeywords);
 
   std::vector<v8::Local<v8::Value>> result(nKeywords, v8::Local<v8::Value>());
   size_t size = std::min(originals.size(), nKeywords);
diff --git a/fxjs/js_define.h b/fxjs/js_define.h
index 2c15c9f..5b6573a 100644
--- a/fxjs/js_define.h
+++ b/fxjs/js_define.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef FXJS_JS_DEFINE_H_
 #define FXJS_JS_DEFINE_H_
 
+#include <memory>
 #include <vector>
 
 #include "core/fxcrt/unowned_ptr.h"
@@ -14,11 +15,10 @@
 #include "fxjs/cjs_result.h"
 #include "fxjs/cjs_runtime.h"
 #include "fxjs/js_resources.h"
-#include "third_party/base/ptr_util.h"
 
 class CJS_Object;
 
-double JS_DateParse(const WideString& str);
+double JS_DateParse(v8::Isolate* pIsolate, const WideString& str);
 
 // Some JS methods have the bizarre convention that they may also be called
 // with a single argument which is an object containing the actual arguments
@@ -42,20 +42,22 @@
 // to construct native object state.
 
 template <class T>
-static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+static void JSConstructor(CFXJS_Engine* pEngine,
+                          v8::Local<v8::Object> obj,
+                          v8::Local<v8::Object> proxy) {
   pEngine->SetObjectPrivate(
-      obj, pdfium::MakeUnique<T>(obj, static_cast<CJS_Runtime*>(pEngine)));
+      obj, std::make_unique<T>(proxy, static_cast<CJS_Runtime*>(pEngine)));
 }
 
 // CJS_Object has virtual dtor, template not required.
 void JSDestructor(v8::Local<v8::Object> obj);
 
 template <class C>
-UnownedPtr<C> JSGetObject(v8::Local<v8::Object> obj) {
+UnownedPtr<C> JSGetObject(v8::Isolate* isolate, v8::Local<v8::Object> obj) {
   if (CFXJS_Engine::GetObjDefnID(obj) != C::GetObjDefnID())
     return nullptr;
 
-  CJS_Object* pJSObj = CFXJS_Engine::GetObjectPrivate(obj);
+  CJS_Object* pJSObj = CFXJS_Engine::GetObjectPrivate(isolate, obj);
   if (!pJSObj)
     return nullptr;
 
@@ -67,7 +69,7 @@
                   const char* class_name_string,
                   v8::Local<v8::String> property,
                   const v8::PropertyCallbackInfo<v8::Value>& info) {
-  auto pObj = JSGetObject<C>(info.Holder());
+  auto pObj = JSGetObject<C>(info.GetIsolate(), info.Holder());
   if (!pObj)
     return;
 
@@ -75,7 +77,7 @@
   if (!pRuntime)
     return;
 
-  CJS_Result result = (pObj.Get()->*M)(pRuntime);
+  CJS_Result result = (pObj.get()->*M)(pRuntime);
   if (result.HasError()) {
     pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string,
                                         result.Error()));
@@ -92,7 +94,7 @@
                   v8::Local<v8::String> property,
                   v8::Local<v8::Value> value,
                   const v8::PropertyCallbackInfo<void>& info) {
-  auto pObj = JSGetObject<C>(info.Holder());
+  auto pObj = JSGetObject<C>(info.GetIsolate(), info.Holder());
   if (!pObj)
     return;
 
@@ -100,7 +102,7 @@
   if (!pRuntime)
     return;
 
-  CJS_Result result = (pObj.Get()->*M)(pRuntime, value);
+  CJS_Result result = (pObj.get()->*M)(pRuntime, value);
   if (result.HasError()) {
     pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string,
                                         result.Error()));
@@ -113,7 +115,7 @@
 void JSMethod(const char* method_name_string,
               const char* class_name_string,
               const v8::FunctionCallbackInfo<v8::Value>& info) {
-  auto pObj = JSGetObject<C>(info.Holder());
+  auto pObj = JSGetObject<C>(info.GetIsolate(), info.Holder());
   if (!pObj)
     return;
 
@@ -125,7 +127,7 @@
   for (unsigned int i = 0; i < (unsigned int)info.Length(); i++)
     parameters.push_back(info[i]);
 
-  CJS_Result result = (pObj.Get()->*M)(pRuntime, parameters);
+  CJS_Result result = (pObj.get()->*M)(pRuntime, parameters);
   if (result.HasError()) {
     pRuntime->Error(JSFormatErrorString(class_name_string, method_name_string,
                                         result.Error()));
diff --git a/fxjs/js_resources.cpp b/fxjs/js_resources.cpp
index 3bc8df3..c133610 100644
--- a/fxjs/js_resources.cpp
+++ b/fxjs/js_resources.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "fxjs/js_resources.h"
 
+#include "third_party/base/notreached.h"
+
 WideString JSGetStringFromID(JSMessage msg) {
   const char* msg_string = "";
   switch (msg) {
@@ -81,12 +83,15 @@
     case JSMessage::kUserGestureRequiredError:
       msg_string = "User gesture required.";
       break;
-    case JSMessage::kTooManyOccurances:
-      msg_string = "Too many occurances.";
+    case JSMessage::kTooManyOccurrences:
+      msg_string = "Too many occurrences.";
       break;
     case JSMessage::kUnknownMethod:
       msg_string = "Unknown method.";
       break;
+    case JSMessage::kWouldBeCyclic:
+      msg_string = "Operation would create a cycle.";
+      break;
     default:
       NOTREACHED();
       break;
@@ -97,10 +102,10 @@
 WideString JSFormatErrorString(const char* class_name,
                                const char* property_name,
                                const WideString& details) {
-  WideString result = WideString::FromDefANSI(class_name);
+  WideString result = WideString::FromUTF8(class_name);
   if (property_name) {
     result += L".";
-    result += WideString::FromDefANSI(property_name);
+    result += WideString::FromUTF8(property_name);
   }
   result += L": ";
   result += details;
diff --git a/fxjs/js_resources.h b/fxjs/js_resources.h
index 8a30862..0bc6be0 100644
--- a/fxjs/js_resources.h
+++ b/fxjs/js_resources.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -33,8 +33,9 @@
   kUnknownProperty,
   kInvalidSetError,
   kUserGestureRequiredError,
-  kTooManyOccurances,
+  kTooManyOccurrences,
   kUnknownMethod,
+  kWouldBeCyclic,
 };
 
 WideString JSGetStringFromID(JSMessage msg);
diff --git a/fxjs/xfa/cfxjse_app_embeddertest.cpp b/fxjs/xfa/cfxjse_app_embeddertest.cpp
index 628f56e..0e6fa45 100644
--- a/fxjs/xfa/cfxjse_app_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_app_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/xfa/cfxjse_arguments.cpp b/fxjs/xfa/cfxjse_arguments.cpp
deleted file mode 100644
index 8bfc8e3..0000000
--- a/fxjs/xfa/cfxjse_arguments.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 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
-
-#include "fxjs/xfa/cfxjse_arguments.h"
-
-#include "fxjs/xfa/cfxjse_context.h"
-#include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
-
-CFXJSE_Arguments::CFXJSE_Arguments(
-    const v8::FunctionCallbackInfo<v8::Value>* pInfo,
-    CFXJSE_Value* pRetValue)
-    : m_pInfo(pInfo), m_pRetValue(pRetValue) {}
-
-CFXJSE_Arguments::~CFXJSE_Arguments() {}
-
-int32_t CFXJSE_Arguments::GetLength() const {
-  return m_pInfo->Length();
-}
-
-std::unique_ptr<CFXJSE_Value> CFXJSE_Arguments::GetValue(int32_t index) const {
-  auto pArgValue = pdfium::MakeUnique<CFXJSE_Value>(v8::Isolate::GetCurrent());
-  pArgValue->ForceSetValue((*m_pInfo)[index]);
-  return pArgValue;
-}
-
-bool CFXJSE_Arguments::GetBoolean(int32_t index) const {
-  return (*m_pInfo)[index]->BooleanValue(m_pInfo->GetIsolate());
-}
-
-int32_t CFXJSE_Arguments::GetInt32(int32_t index) const {
-  return static_cast<int32_t>(
-      (*m_pInfo)[index]
-          ->NumberValue(m_pInfo->GetIsolate()->GetCurrentContext())
-          .FromMaybe(0.0));
-}
-
-float CFXJSE_Arguments::GetFloat(int32_t index) const {
-  return static_cast<float>(
-      (*m_pInfo)[index]
-          ->NumberValue(m_pInfo->GetIsolate()->GetCurrentContext())
-          .FromMaybe(0.0));
-}
-
-ByteString CFXJSE_Arguments::GetUTF8String(int32_t index) const {
-  v8::Isolate* isolate = m_pInfo->GetIsolate();
-  v8::Local<v8::Value> info = (*m_pInfo)[index];
-  v8::Local<v8::String> hString =
-      info->ToString(isolate->GetCurrentContext()).ToLocalChecked();
-  v8::String::Utf8Value szStringVal(isolate, hString);
-  return ByteString(*szStringVal);
-}
-
-CFXJSE_Value* CFXJSE_Arguments::GetReturnValue() const {
-  return m_pRetValue.Get();
-}
diff --git a/fxjs/xfa/cfxjse_arguments.h b/fxjs/xfa/cfxjse_arguments.h
deleted file mode 100644
index e048bc2..0000000
--- a/fxjs/xfa/cfxjse_arguments.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 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 FXJS_XFA_CFXJSE_ARGUMENTS_H_
-#define FXJS_XFA_CFXJSE_ARGUMENTS_H_
-
-#include <memory>
-
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/unowned_ptr.h"
-#include "v8/include/v8.h"
-
-class CFXJSE_Value;
-
-class CFXJSE_Arguments {
- public:
-  CFXJSE_Arguments(const v8::FunctionCallbackInfo<v8::Value>* pInfo,
-                   CFXJSE_Value* pRetValue);
-  ~CFXJSE_Arguments();
-
-  int32_t GetLength() const;
-  std::unique_ptr<CFXJSE_Value> GetValue(int32_t index) const;
-  bool GetBoolean(int32_t index) const;
-  int32_t GetInt32(int32_t index) const;
-  float GetFloat(int32_t index) const;
-  ByteString GetUTF8String(int32_t index) const;
-  CFXJSE_Value* GetReturnValue() const;
-
- private:
-  UnownedPtr<const v8::FunctionCallbackInfo<v8::Value>> const m_pInfo;
-  UnownedPtr<CFXJSE_Value> const m_pRetValue;
-};
-
-#endif  // FXJS_XFA_CFXJSE_ARGUMENTS_H_
diff --git a/fxjs/xfa/cfxjse_class.cpp b/fxjs/xfa/cfxjse_class.cpp
index f6caf07..26b4666 100644
--- a/fxjs/xfa/cfxjse_class.cpp
+++ b/fxjs/xfa/cfxjse_class.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,21 @@
 #include <utility>
 
 #include "fxjs/cjs_result.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
-#include "fxjs/xfa/cfxjse_arguments.h"
 #include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-external.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-template.h"
 
 using pdfium::fxjse::kClassTag;
 using pdfium::fxjse::kFuncTag;
@@ -34,19 +43,12 @@
 
 void V8FunctionCallback_Wrapper(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo =
+  const FXJSE_FUNCTION_DESCRIPTOR* pFunctionInfo =
       AsFunctionDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpFunctionInfo)
+  if (!pFunctionInfo)
     return;
 
-  ByteStringView szFunctionName(lpFunctionInfo->name);
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(info.Holder());
-  auto lpRetValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  CFXJSE_Arguments impl(&info, lpRetValue.get());
-  lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl);
-  if (!lpRetValue->DirectGetValue().IsEmpty())
-    info.GetReturnValue().Set(lpRetValue->DirectGetValue());
+  pFunctionInfo->callbackProc(CFXJSE_HostObject::FromV8(info.Holder()), info);
 }
 
 void V8ConstructorCallback_Wrapper(
@@ -54,30 +56,28 @@
   if (!info.IsConstructCall())
     return;
 
-  const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition =
+  const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClassDefinition)
+  if (!pClassDescriptor)
     return;
 
-  ASSERT(info.Holder()->InternalFieldCount() == 2);
+  DCHECK_EQ(info.Holder()->InternalFieldCount(), 2);
   info.Holder()->SetAlignedPointerInInternalField(0, nullptr);
   info.Holder()->SetAlignedPointerInInternalField(1, nullptr);
 }
 
 void Context_GlobalObjToString(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+  const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClass)
+  if (!pClassDescriptor)
     return;
 
-  if (info.This() == info.Holder() && lpClass->name) {
-    ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name);
+  if (info.This() == info.Holder() && pClassDescriptor->name) {
+    ByteString szStringVal =
+        ByteString::Format("[object %s]", pClassDescriptor->name);
     info.GetReturnValue().Set(
-        v8::String::NewFromUtf8(info.GetIsolate(), szStringVal.c_str(),
-                                v8::NewStringType::kNormal,
-                                szStringVal.GetLength())
-            .ToLocalChecked());
+        fxv8::NewStringHelper(info.GetIsolate(), szStringVal.AsStringView()));
     return;
   }
   v8::Local<v8::String> local_str =
@@ -95,10 +95,10 @@
 
   auto* pClassDescriptor = static_cast<const FXJSE_CLASS_DESCRIPTOR*>(
       hCallBackInfo->GetAlignedPointerFromInternalField(0));
-  if (pClassDescriptor != &GlobalClassDescriptor &&
-      pClassDescriptor != &NormalClassDescriptor &&
-      pClassDescriptor != &VariablesClassDescriptor &&
-      pClassDescriptor != &kFormCalcFM2JSDescriptor) {
+  if (pClassDescriptor != &kGlobalClassDescriptor &&
+      pClassDescriptor != &kNormalClassDescriptor &&
+      pClassDescriptor != &kVariablesClassDescriptor &&
+      pClassDescriptor != &kFormCalcDescriptor) {
     return;
   }
 
@@ -114,9 +114,7 @@
   if (result.HasError()) {
     WideString err = JSFormatErrorString(pClassDescriptor->name, *szPropName,
                                          result.Error());
-    v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8(
-        info.GetIsolate(), err.ToDefANSI().c_str(), v8::NewStringType::kNormal);
-    info.GetIsolate()->ThrowException(str.ToLocalChecked());
+    fxv8::ThrowExceptionHelper(info.GetIsolate(), err.AsStringView());
     return;
   }
 
@@ -124,39 +122,39 @@
     info.GetReturnValue().Set(result.Return());
 }
 
-void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                          CFXJSE_Value* pObject,
+void DynPropGetterAdapter(v8::Isolate* pIsolate,
+                          const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
+                          v8::Local<v8::Object> pObject,
                           ByteStringView szPropName,
                           CFXJSE_Value* pValue) {
-  ASSERT(lpClass);
+  DCHECK(pClassDescriptor);
 
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, false);
-  if (nPropType == FXJSE_ClassPropType_Property) {
-    if (lpClass->dynPropGetter)
-      lpClass->dynPropGetter(pObject, szPropName, pValue);
-  } else if (nPropType == FXJSE_ClassPropType_Method) {
-    if (lpClass->dynMethodCall && pValue) {
-      v8::Isolate* pIsolate = pValue->GetIsolate();
+  FXJSE_ClassPropType nPropType =
+      pClassDescriptor->dynPropTypeGetter
+          ? pClassDescriptor->dynPropTypeGetter(pIsolate, pObject, szPropName,
+                                                false)
+          : FXJSE_ClassPropType::kProperty;
+  if (nPropType == FXJSE_ClassPropType::kProperty) {
+    if (pClassDescriptor->dynPropGetter) {
+      pValue->ForceSetValue(pIsolate, pClassDescriptor->dynPropGetter(
+                                          pIsolate, pObject, szPropName));
+    }
+  } else if (nPropType == FXJSE_ClassPropType::kMethod) {
+    if (pClassDescriptor->dynMethodCall && pValue) {
       v8::HandleScope hscope(pIsolate);
       v8::Local<v8::ObjectTemplate> hCallBackInfoTemplate =
           v8::ObjectTemplate::New(pIsolate);
       hCallBackInfoTemplate->SetInternalFieldCount(2);
       v8::Local<v8::Object> hCallBackInfo =
-          hCallBackInfoTemplate
-              ->NewInstance(pValue->GetIsolate()->GetCurrentContext())
+          hCallBackInfoTemplate->NewInstance(pIsolate->GetCurrentContext())
               .ToLocalChecked();
       hCallBackInfo->SetAlignedPointerInInternalField(
-          0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClass));
+          0, const_cast<FXJSE_CLASS_DESCRIPTOR*>(pClassDescriptor));
       hCallBackInfo->SetInternalField(
-          1, v8::String::NewFromUtf8(
-                 pIsolate, reinterpret_cast<const char*>(szPropName.raw_str()),
-                 v8::NewStringType::kNormal, szPropName.GetLength())
-                 .ToLocalChecked());
+          1, fxv8::NewStringHelper(pIsolate, szPropName));
       pValue->ForceSetValue(
-          v8::Function::New(pValue->GetIsolate()->GetCurrentContext(),
+          pIsolate,
+          v8::Function::New(pIsolate->GetCurrentContext(),
                             DynPropGetterAdapter_MethodCallback, hCallBackInfo,
                             0, v8::ConstructorBehavior::kThrow)
               .ToLocalChecked());
@@ -164,47 +162,49 @@
   }
 }
 
-void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                          CFXJSE_Value* pObject,
+void DynPropSetterAdapter(v8::Isolate* pIsolate,
+                          const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
+                          v8::Local<v8::Object> pObject,
                           ByteStringView szPropName,
                           CFXJSE_Value* pValue) {
-  ASSERT(lpClass);
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, false);
-  if (nPropType != FXJSE_ClassPropType_Method) {
-    if (lpClass->dynPropSetter)
-      lpClass->dynPropSetter(pObject, szPropName, pValue);
+  DCHECK(pClassDescriptor);
+  FXJSE_ClassPropType nPropType =
+      pClassDescriptor->dynPropTypeGetter
+          ? pClassDescriptor->dynPropTypeGetter(pIsolate, pObject, szPropName,
+                                                false)
+          : FXJSE_ClassPropType::kProperty;
+  if (nPropType != FXJSE_ClassPropType::kMethod) {
+    if (pClassDescriptor->dynPropSetter) {
+      pClassDescriptor->dynPropSetter(pIsolate, pObject, szPropName,
+                                      pValue->GetValue(pIsolate));
+    }
   }
 }
 
-bool DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass,
-                         CFXJSE_Value* pObject,
+bool DynPropQueryAdapter(v8::Isolate* pIsolate,
+                         const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
+                         v8::Local<v8::Object> pObject,
                          ByteStringView szPropName) {
-  ASSERT(lpClass);
-  int32_t nPropType =
-      lpClass->dynPropTypeGetter == nullptr
-          ? FXJSE_ClassPropType_Property
-          : lpClass->dynPropTypeGetter(pObject, szPropName, true);
-  return nPropType != FXJSE_ClassPropType_None;
+  FXJSE_ClassPropType nPropType = pClassDescriptor->dynPropTypeGetter
+                                      ? pClassDescriptor->dynPropTypeGetter(
+                                            pIsolate, pObject, szPropName, true)
+                                      : FXJSE_ClassPropType::kProperty;
+  return nPropType != FXJSE_ClassPropType::kNone;
 }
 
 void NamedPropertyQueryCallback(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+  const FXJSE_CLASS_DESCRIPTOR* pClass =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClass)
+  if (!pClass)
     return;
 
   v8::HandleScope scope(info.GetIsolate());
   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
   ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-  if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) {
+  if (DynPropQueryAdapter(info.GetIsolate(), pClass, info.Holder(),
+                          szFxPropName)) {
     info.GetReturnValue().Set(v8::DontDelete);
     return;
   }
@@ -215,40 +215,33 @@
 void NamedPropertyGetterCallback(
     v8::Local<v8::Name> property,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+  const FXJSE_CLASS_DESCRIPTOR* pClass =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClass)
+  if (!pClass)
     return;
 
   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
   ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-  auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
-                       lpNewValue.get());
-  info.GetReturnValue().Set(lpNewValue->DirectGetValue());
+  auto pNewValue = std::make_unique<CFXJSE_Value>();
+  DynPropGetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName,
+                       pNewValue.get());
+  info.GetReturnValue().Set(pNewValue->DirectGetValue());
 }
 
 void NamedPropertySetterCallback(
     v8::Local<v8::Name> property,
     v8::Local<v8::Value> value,
     const v8::PropertyCallbackInfo<v8::Value>& info) {
-  v8::Local<v8::Object> thisObject = info.Holder();
-  const FXJSE_CLASS_DESCRIPTOR* lpClass =
+  const FXJSE_CLASS_DESCRIPTOR* pClass =
       AsClassDescriptor(info.Data().As<v8::External>()->Value());
-  if (!lpClass)
+  if (!pClass)
     return;
 
   v8::String::Utf8Value szPropName(info.GetIsolate(), property);
   ByteStringView szFxPropName(*szPropName, szPropName.length());
-  auto lpThisValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpThisValue->ForceSetValue(thisObject);
-  auto lpNewValue = pdfium::MakeUnique<CFXJSE_Value>(info.GetIsolate());
-  lpNewValue->ForceSetValue(value);
-  DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName,
-                       lpNewValue.get());
+  auto pNewValue = std::make_unique<CFXJSE_Value>(info.GetIsolate(), value);
+  DynPropSetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName,
+                       pNewValue.get());
   info.GetReturnValue().Set(value);
 }
 
@@ -258,86 +251,89 @@
 }
 
 void SetUpNamedPropHandler(v8::Isolate* pIsolate,
-                           v8::Local<v8::ObjectTemplate>* pObjectTemplate,
-                           const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) {
+                           v8::Local<v8::ObjectTemplate> pObjectTemplate,
+                           const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor) {
   v8::NamedPropertyHandlerConfiguration configuration(
-      lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : nullptr,
-      lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : nullptr,
-      lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback
-                                           : nullptr,
+      pClassDescriptor->dynPropGetter ? NamedPropertyGetterCallback : nullptr,
+      pClassDescriptor->dynPropSetter ? NamedPropertySetterCallback : nullptr,
+      pClassDescriptor->dynPropTypeGetter ? NamedPropertyQueryCallback
+                                          : nullptr,
       nullptr, NamedPropertyEnumeratorCallback,
       v8::External::New(pIsolate,
-                        const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)),
+                        const_cast<FXJSE_CLASS_DESCRIPTOR*>(pClassDescriptor)),
       v8::PropertyHandlerFlags::kNonMasking);
-  (*pObjectTemplate)->SetHandler(configuration);
+  pObjectTemplate->SetHandler(configuration);
 }
 
 }  // namespace
 
 // static
 CFXJSE_Class* CFXJSE_Class::Create(
-    CFXJSE_Context* lpContext,
-    const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition,
+    CFXJSE_Context* pContext,
+    const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
     bool bIsJSGlobal) {
-  if (!lpContext || !lpClassDefinition)
+  if (!pContext || !pClassDescriptor)
     return nullptr;
 
   CFXJSE_Class* pExistingClass =
-      lpContext->GetClassByName(lpClassDefinition->name);
+      pContext->GetClassByName(pClassDescriptor->name);
   if (pExistingClass)
     return pExistingClass;
 
-  v8::Isolate* pIsolate = lpContext->GetIsolate();
-  auto pClass = pdfium::MakeUnique<CFXJSE_Class>(lpContext);
-  pClass->m_szClassName = lpClassDefinition->name;
-  pClass->m_lpClassDefinition = lpClassDefinition;
+  v8::Isolate* pIsolate = pContext->GetIsolate();
+  auto pClass = std::make_unique<CFXJSE_Class>(pContext);
+  pClass->m_szClassName = pClassDescriptor->name;
+  pClass->m_pClassDescriptor = pClassDescriptor;
   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
   v8::Local<v8::FunctionTemplate> hFunctionTemplate = v8::FunctionTemplate::New(
       pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper,
-      v8::External::New(
-          pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
-  hFunctionTemplate->SetClassName(
-      v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name,
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked());
+      v8::External::New(pIsolate,
+                        const_cast<FXJSE_CLASS_DESCRIPTOR*>(pClassDescriptor)));
+  v8::Local<v8::String> classname =
+      fxv8::NewStringHelper(pIsolate, pClassDescriptor->name);
+  hFunctionTemplate->SetClassName(classname);
+  hFunctionTemplate->PrototypeTemplate()->Set(
+      v8::Symbol::GetToStringTag(pIsolate), classname,
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
   hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2);
   v8::Local<v8::ObjectTemplate> hObjectTemplate =
       hFunctionTemplate->InstanceTemplate();
-  SetUpNamedPropHandler(pIsolate, &hObjectTemplate, lpClassDefinition);
+  SetUpNamedPropHandler(pIsolate, hObjectTemplate, pClassDescriptor);
 
-  if (lpClassDefinition->methNum) {
-    for (int32_t i = 0; i < lpClassDefinition->methNum; i++) {
+  if (pClassDescriptor->methNum) {
+    for (int32_t i = 0; i < pClassDescriptor->methNum; i++) {
       v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
           pIsolate, V8FunctionCallback_Wrapper,
           v8::External::New(pIsolate, const_cast<FXJSE_FUNCTION_DESCRIPTOR*>(
-                                          lpClassDefinition->methods + i)));
+                                          pClassDescriptor->methods + i)));
       fun->RemovePrototype();
       hObjectTemplate->Set(
-          v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name,
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked(),
+          fxv8::NewStringHelper(pIsolate, pClassDescriptor->methods[i].name),
           fun,
           static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
     }
   }
 
   if (bIsJSGlobal) {
-    v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
+    v8::Local<v8::FunctionTemplate> fn = v8::FunctionTemplate::New(
         pIsolate, Context_GlobalObjToString,
         v8::External::New(
-            pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(lpClassDefinition)));
-    fun->RemovePrototype();
-    hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString",
-                                                 v8::NewStringType::kNormal)
-                             .ToLocalChecked(),
-                         fun);
+            pIsolate, const_cast<FXJSE_CLASS_DESCRIPTOR*>(pClassDescriptor)));
+    fn->RemovePrototype();
+    hObjectTemplate->Set(fxv8::NewStringHelper(pIsolate, "toString"), fn);
   }
-  pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate);
+  pClass->m_hTemplate.Reset(pContext->GetIsolate(), hFunctionTemplate);
   CFXJSE_Class* pResult = pClass.get();
-  lpContext->AddClass(std::move(pClass));
+  pContext->AddClass(std::move(pClass));
   return pResult;
 }
 
-CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext) : m_pContext(lpContext) {}
+CFXJSE_Class::CFXJSE_Class(const CFXJSE_Context* pContext)
+    : m_pContext(pContext) {}
 
-CFXJSE_Class::~CFXJSE_Class() {}
+CFXJSE_Class::~CFXJSE_Class() = default;
+
+v8::Local<v8::FunctionTemplate> CFXJSE_Class::GetTemplate(
+    v8::Isolate* pIsolate) {
+  return v8::Local<v8::FunctionTemplate>::New(pIsolate, m_hTemplate);
+}
diff --git a/fxjs/xfa/cfxjse_class.h b/fxjs/xfa/cfxjse_class.h
index f5bc232..5f4d752 100644
--- a/fxjs/xfa/cfxjse_class.h
+++ b/fxjs/xfa/cfxjse_class.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,31 +9,29 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/xfa/fxjse.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
 class CFXJSE_Context;
-class CFXJSE_Value;
 struct FXJSE_CLASS_DESCRIPTOR;
 
 class CFXJSE_Class {
  public:
   static CFXJSE_Class* Create(CFXJSE_Context* pContext,
-                              const FXJSE_CLASS_DESCRIPTOR* lpClassDefintion,
+                              const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor,
                               bool bIsJSGlobal);
 
-  explicit CFXJSE_Class(CFXJSE_Context* lpContext);
+  explicit CFXJSE_Class(const CFXJSE_Context* pContext);
   ~CFXJSE_Class();
 
-  CFXJSE_Context* GetContext() const { return m_pContext.Get(); }
-  v8::Global<v8::FunctionTemplate>& GetTemplate() { return m_hTemplate; }
+  bool IsName(ByteStringView name) const { return name == m_szClassName; }
+  const CFXJSE_Context* GetContext() const { return m_pContext; }
+  v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* pIsolate);
 
  protected:
-  friend class CFXJSE_Context;
-  friend class CFXJSE_Value;
-
   ByteString m_szClassName;
-  UnownedPtr<const FXJSE_CLASS_DESCRIPTOR> m_lpClassDefinition;
-  UnownedPtr<CFXJSE_Context> const m_pContext;
+  UnownedPtr<const FXJSE_CLASS_DESCRIPTOR> m_pClassDescriptor;
+  UnownedPtr<const CFXJSE_Context> const m_pContext;
   v8::Global<v8::FunctionTemplate> m_hTemplate;
 };
 
diff --git a/fxjs/xfa/cfxjse_context.cpp b/fxjs/xfa/cfxjse_context.cpp
index 1ff1417..970cb3d 100644
--- a/fxjs/xfa/cfxjse_context.cpp
+++ b/fxjs/xfa/cfxjse_context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,11 +9,20 @@
 #include <utility>
 
 #include "fxjs/cfxjs_engine.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_runtimedata.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 #include "third_party/base/ptr_util.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-message.h"
+#include "v8/include/v8-script.h"
+#include "xfa/fxfa/parser/cxfa_thisproxy.h"
 
 namespace {
 
@@ -51,9 +60,12 @@
     "  this.log(...args);\n"
     "};";
 
-// Only address matters, values are for humans debuging here.
-char g_FXJSEHostObjectTag[] = "FXJSE Host Object";
-char g_FXJSEProxyObjectTag[] = "FXJSE Proxy Object";
+// Only address matters, values are for humans debuging here.  Keep these
+// wchar_t to prevent the compiler from doing something clever, like
+// aligning them on a byte boundary to save space, which would make them
+// incompatible for use as V8 aligned pointers.
+const wchar_t kFXJSEHostObjectTag[] = L"FXJSE Host Object";
+const wchar_t kFXJSEProxyObjectTag[] = L"FXJSE Proxy Object";
 
 v8::Local<v8::Object> CreateReturnValue(v8::Isolate* pIsolate,
                                         v8::TryCatch* trycatch) {
@@ -68,23 +80,18 @@
   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
   v8::Local<v8::Value> hException = trycatch->Exception();
   if (hException->IsObject()) {
-    v8::Local<v8::String> hNameStr =
-        v8::String::NewFromUtf8(pIsolate, "name", v8::NewStringType::kNormal)
-            .ToLocalChecked();
+    v8::Local<v8::String> hNameStr = fxv8::NewStringHelper(pIsolate, "name");
     v8::Local<v8::Value> hValue =
         hException.As<v8::Object>()->Get(context, hNameStr).ToLocalChecked();
     if (hValue->IsString() || hValue->IsStringObject()) {
       hReturnValue->Set(context, 0, hValue).FromJust();
     } else {
       v8::Local<v8::String> hErrorStr =
-          v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal)
-              .ToLocalChecked();
+          fxv8::NewStringHelper(pIsolate, "Error");
       hReturnValue->Set(context, 0, hErrorStr).FromJust();
     }
-
     v8::Local<v8::String> hMessageStr =
-        v8::String::NewFromUtf8(pIsolate, "message", v8::NewStringType::kNormal)
-            .ToLocalChecked();
+        fxv8::NewStringHelper(pIsolate, "message");
     hValue =
         hException.As<v8::Object>()->Get(context, hMessageStr).ToLocalChecked();
     if (hValue->IsString() || hValue->IsStringObject())
@@ -92,9 +99,7 @@
     else
       hReturnValue->Set(context, 1, hMessage->Get()).FromJust();
   } else {
-    v8::Local<v8::String> hErrorStr =
-        v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal)
-            .ToLocalChecked();
+    v8::Local<v8::String> hErrorStr = fxv8::NewStringHelper(pIsolate, "Error");
     hReturnValue->Set(context, 0, hErrorStr).FromJust();
     hReturnValue->Set(context, 1, hMessage->Get()).FromJust();
   }
@@ -111,65 +116,48 @@
   return hReturnValue;
 }
 
-class CFXJSE_ScopeUtil_IsolateHandleContext {
- public:
-  explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext)
-      : m_parent(pContext->GetIsolate()), m_cscope(pContext->GetContext()) {}
-  CFXJSE_ScopeUtil_IsolateHandleContext(
-      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
-  CFXJSE_ScopeUtil_IsolateHandleContext& operator=(
-      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
-
- private:
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  CFXJSE_ScopeUtil_IsolateHandle m_parent;
-  v8::Context::Scope m_cscope;
-};
-
 void FXJSE_UpdateProxyBinding(v8::Local<v8::Object> hObject) {
-  ASSERT(!hObject.IsEmpty());
-  ASSERT(hObject->InternalFieldCount() == 2);
-  hObject->SetAlignedPointerInInternalField(0, g_FXJSEProxyObjectTag);
+  DCHECK(!hObject.IsEmpty());
+  DCHECK_EQ(hObject->InternalFieldCount(), 2);
+  hObject->SetAlignedPointerInInternalField(
+      0, const_cast<wchar_t*>(kFXJSEProxyObjectTag));
   hObject->SetAlignedPointerInInternalField(1, nullptr);
 }
 
 }  // namespace
 
 void FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,
-                               CFXJSE_HostObject* lpNewBinding) {
-  ASSERT(!hObject.IsEmpty());
-  ASSERT(hObject->InternalFieldCount() == 2);
-  hObject->SetAlignedPointerInInternalField(0, g_FXJSEHostObjectTag);
-  hObject->SetAlignedPointerInInternalField(1, lpNewBinding);
+                               CFXJSE_HostObject* pNewBinding) {
+  DCHECK(!hObject.IsEmpty());
+  DCHECK_EQ(hObject->InternalFieldCount(), 2);
+  hObject->SetAlignedPointerInInternalField(
+      0, const_cast<wchar_t*>(kFXJSEHostObjectTag));
+  hObject->SetAlignedPointerInInternalField(1, pNewBinding);
 }
 
 void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hObject) {
-  ASSERT(!hObject.IsEmpty());
-  ASSERT(hObject->InternalFieldCount() == 2);
+  DCHECK(!hObject.IsEmpty());
+  DCHECK_EQ(hObject->InternalFieldCount(), 2);
   hObject->SetAlignedPointerInInternalField(0, nullptr);
   hObject->SetAlignedPointerInInternalField(1, nullptr);
 }
 
-CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(
-    v8::Local<v8::Object> hJSObject) {
-  ASSERT(!hJSObject.IsEmpty());
-  if (!hJSObject->IsObject())
+CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Value> hValue) {
+  if (!fxv8::IsObject(hValue))
     return nullptr;
 
-  v8::Local<v8::Object> hObject = hJSObject;
+  v8::Local<v8::Object> hObject = hValue.As<v8::Object>();
   if (hObject->InternalFieldCount() != 2 ||
-      hObject->GetAlignedPointerFromInternalField(0) == g_FXJSEProxyObjectTag) {
+      hObject->GetAlignedPointerFromInternalField(0) == kFXJSEProxyObjectTag) {
     v8::Local<v8::Value> hProtoObject = hObject->GetPrototype();
-    if (hProtoObject.IsEmpty() || !hProtoObject->IsObject())
+    if (!fxv8::IsObject(hProtoObject))
       return nullptr;
 
     hObject = hProtoObject.As<v8::Object>();
     if (hObject->InternalFieldCount() != 2)
       return nullptr;
   }
-  if (hObject->GetAlignedPointerFromInternalField(0) != g_FXJSEHostObjectTag)
+  if (hObject->GetAlignedPointerFromInternalField(0) != kFXJSEHostObjectTag)
     return nullptr;
 
   return static_cast<CFXJSE_HostObject*>(
@@ -180,31 +168,27 @@
 std::unique_ptr<CFXJSE_Context> CFXJSE_Context::Create(
     v8::Isolate* pIsolate,
     const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
-    CFXJSE_HostObject* pGlobalObject) {
+    CFXJSE_HostObject* pGlobalObject,
+    CXFA_ThisProxy* pProxy) {
   CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
-  auto pContext = pdfium::MakeUnique<CFXJSE_Context>(pIsolate);
 
+  // Private constructor.
+  auto pContext = pdfium::WrapUnique(new CFXJSE_Context(pIsolate, pProxy));
   v8::Local<v8::ObjectTemplate> hObjectTemplate;
   if (pGlobalClass) {
     CFXJSE_Class* pGlobalClassObj =
         CFXJSE_Class::Create(pContext.get(), pGlobalClass, true);
-    ASSERT(pGlobalClassObj);
-    v8::Local<v8::FunctionTemplate> hFunctionTemplate =
-        v8::Local<v8::FunctionTemplate>::New(pIsolate,
-                                             pGlobalClassObj->m_hTemplate);
-    hObjectTemplate = hFunctionTemplate->InstanceTemplate();
+    hObjectTemplate =
+        pGlobalClassObj->GetTemplate(pIsolate)->InstanceTemplate();
   } else {
     hObjectTemplate = v8::ObjectTemplate::New(pIsolate);
     hObjectTemplate->SetInternalFieldCount(2);
   }
-  hObjectTemplate->Set(
-      v8::Symbol::GetToStringTag(pIsolate),
-      v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-          .ToLocalChecked());
+  hObjectTemplate->Set(v8::Symbol::GetToStringTag(pIsolate),
+                       fxv8::NewStringHelper(pIsolate, "global"));
 
   v8::Local<v8::Context> hNewContext =
       v8::Context::New(pIsolate, nullptr, hObjectTemplate);
-
   v8::Local<v8::Object> pThisProxy = hNewContext->Global();
   FXJSE_UpdateProxyBinding(pThisProxy);
 
@@ -212,25 +196,25 @@
   FXJSE_UpdateObjectBinding(pThis, pGlobalObject);
 
   v8::Local<v8::Context> hRootContext = v8::Local<v8::Context>::New(
-      pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext);
+      pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->GetRootContext());
   hNewContext->SetSecurityToken(hRootContext->GetSecurityToken());
   pContext->m_hContext.Reset(pIsolate, hNewContext);
   return pContext;
 }
 
-CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
+CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate, CXFA_ThisProxy* pProxy)
+    : m_pIsolate(pIsolate), m_pProxy(pProxy) {}
 
-CFXJSE_Context::~CFXJSE_Context() {}
+CFXJSE_Context::~CFXJSE_Context() = default;
 
-std::unique_ptr<CFXJSE_Value> CFXJSE_Context::GetGlobalObject() {
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
+v8::Local<v8::Object> CFXJSE_Context::GetGlobalObject() {
+  v8::Isolate::Scope isolate_scope(GetIsolate());
+  v8::EscapableHandleScope handle_scope(GetIsolate());
   v8::Local<v8::Context> hContext =
       v8::Local<v8::Context>::New(GetIsolate(), m_hContext);
-  v8::Local<v8::Object> hGlobalObject =
+  v8::Local<v8::Object> result =
       hContext->Global()->GetPrototype().As<v8::Object>();
-  pValue->ForceSetValue(hGlobalObject);
-  return pValue;
+  return handle_scope.Escape(result);
 }
 
 v8::Local<v8::Context> CFXJSE_Context::GetContext() {
@@ -245,64 +229,58 @@
   auto pClass =
       std::find_if(m_rgClasses.begin(), m_rgClasses.end(),
                    [szName](const std::unique_ptr<CFXJSE_Class>& item) {
-                     return szName == item->m_szClassName;
+                     return item->IsName(szName);
                    });
   return pClass != m_rgClasses.end() ? pClass->get() : nullptr;
 }
 
 void CFXJSE_Context::EnableCompatibleMode() {
-  ExecuteScript(szCompatibleModeScript, nullptr, nullptr);
-  ExecuteScript(szConsoleScript, nullptr, nullptr);
+  ExecuteScript(szCompatibleModeScript, nullptr, v8::Local<v8::Object>());
+  ExecuteScript(szConsoleScript, nullptr, v8::Local<v8::Object>());
 }
 
-bool CFXJSE_Context::ExecuteScript(const char* szScript,
-                                   CFXJSE_Value* lpRetValue,
-                                   CFXJSE_Value* lpNewThisObject) {
+bool CFXJSE_Context::ExecuteScript(ByteStringView bsScript,
+                                   CFXJSE_Value* pRetValue,
+                                   v8::Local<v8::Object> hNewThis) {
   CFXJSE_ScopeUtil_IsolateHandleContext scope(this);
   v8::Local<v8::Context> hContext = GetIsolate()->GetCurrentContext();
   v8::TryCatch trycatch(GetIsolate());
   v8::Local<v8::String> hScriptString =
-      v8::String::NewFromUtf8(GetIsolate(), szScript,
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked();
-  if (!lpNewThisObject) {
+      fxv8::NewStringHelper(GetIsolate(), bsScript);
+  if (hNewThis.IsEmpty()) {
     v8::Local<v8::Script> hScript;
     if (v8::Script::Compile(hContext, hScriptString).ToLocal(&hScript)) {
-      ASSERT(!trycatch.HasCaught());
+      CHECK(!trycatch.HasCaught());
       v8::Local<v8::Value> hValue;
       if (hScript->Run(hContext).ToLocal(&hValue)) {
-        ASSERT(!trycatch.HasCaught());
-        if (lpRetValue)
-          lpRetValue->ForceSetValue(hValue);
+        CHECK(!trycatch.HasCaught());
+        if (pRetValue)
+          pRetValue->ForceSetValue(GetIsolate(), hValue);
         return true;
       }
     }
-    if (lpRetValue)
-      lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch));
+    if (pRetValue) {
+      pRetValue->ForceSetValue(GetIsolate(),
+                               CreateReturnValue(GetIsolate(), &trycatch));
+    }
     return false;
   }
 
-  v8::Local<v8::Value> hNewThis = v8::Local<v8::Value>::New(
-      GetIsolate(), lpNewThisObject->DirectGetValue());
-  ASSERT(!hNewThis.IsEmpty());
-  v8::Local<v8::String> hEval =
-      v8::String::NewFromUtf8(GetIsolate(),
-                              "(function () { return eval(arguments[0]); })",
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked();
+  v8::Local<v8::String> hEval = fxv8::NewStringHelper(
+      GetIsolate(), "(function () { return eval(arguments[0]); })");
   v8::Local<v8::Script> hWrapper =
       v8::Script::Compile(hContext, hEval).ToLocalChecked();
   v8::Local<v8::Value> hWrapperValue;
   if (hWrapper->Run(hContext).ToLocal(&hWrapperValue)) {
-    ASSERT(!trycatch.HasCaught());
+    CHECK(!trycatch.HasCaught());
+    CHECK(hWrapperValue->IsFunction());
     v8::Local<v8::Function> hWrapperFn = hWrapperValue.As<v8::Function>();
     v8::Local<v8::Value> rgArgs[] = {hScriptString};
     v8::Local<v8::Value> hValue;
-    if (hWrapperFn->Call(hContext, hNewThis.As<v8::Object>(), 1, rgArgs)
-            .ToLocal(&hValue)) {
-      ASSERT(!trycatch.HasCaught());
-      if (lpRetValue)
-        lpRetValue->ForceSetValue(hValue);
+    if (hWrapperFn->Call(hContext, hNewThis, 1, rgArgs).ToLocal(&hValue)) {
+      DCHECK(!trycatch.HasCaught());
+      if (pRetValue)
+        pRetValue->ForceSetValue(GetIsolate(), hValue);
       return true;
     }
   }
@@ -321,7 +299,9 @@
   }
 #endif  // NDEBUG
 
-  if (lpRetValue)
-    lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch));
+  if (pRetValue) {
+    pRetValue->ForceSetValue(GetIsolate(),
+                             CreateReturnValue(GetIsolate(), &trycatch));
+  }
   return false;
 }
diff --git a/fxjs/xfa/cfxjse_context.h b/fxjs/xfa/cfxjse_context.h
index b519e77..4217b2b 100644
--- a/fxjs/xfa/cfxjse_context.h
+++ b/fxjs/xfa/cfxjse_context.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,14 +10,16 @@
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "v8/include/v8.h"
+#include "v8/include/cppgc/persistent.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
-class CFXJS_Engine;
 class CFXJSE_Class;
 class CFXJSE_HostObject;
 class CFXJSE_Value;
+class CXFA_ThisProxy;
 struct FXJSE_CLASS_DESCRIPTOR;
 
 class CFXJSE_Context {
@@ -25,34 +27,39 @@
   static std::unique_ptr<CFXJSE_Context> Create(
       v8::Isolate* pIsolate,
       const FXJSE_CLASS_DESCRIPTOR* pGlobalClass,
-      CFXJSE_HostObject* pGlobalObject);
+      CFXJSE_HostObject* pGlobalObject,
+      CXFA_ThisProxy* pProxy);
 
-  explicit CFXJSE_Context(v8::Isolate* pIsolate);
   ~CFXJSE_Context();
 
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  v8::Isolate* GetIsolate() const { return m_pIsolate; }
   v8::Local<v8::Context> GetContext();
-  std::unique_ptr<CFXJSE_Value> GetGlobalObject();
+  v8::Local<v8::Object> GetGlobalObject();
+
   void AddClass(std::unique_ptr<CFXJSE_Class> pClass);
   CFXJSE_Class* GetClassByName(ByteStringView szName) const;
   void EnableCompatibleMode();
-  bool ExecuteScript(const char* szScript,
-                     CFXJSE_Value* lpRetValue,
-                     CFXJSE_Value* lpNewThisObject);
+
+  // Note: `pNewThisObject` may be empty.
+  bool ExecuteScript(ByteStringView bsScript,
+                     CFXJSE_Value* pRetValue,
+                     v8::Local<v8::Object> pNewThisObject);
 
  private:
+  CFXJSE_Context(v8::Isolate* pIsolate, CXFA_ThisProxy* pProxy);
   CFXJSE_Context(const CFXJSE_Context&) = delete;
   CFXJSE_Context& operator=(const CFXJSE_Context&) = delete;
 
   v8::Global<v8::Context> m_hContext;
   UnownedPtr<v8::Isolate> m_pIsolate;
   std::vector<std::unique_ptr<CFXJSE_Class>> m_rgClasses;
+  cppgc::Persistent<CXFA_ThisProxy> m_pProxy;
 };
 
 void FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,
-                               CFXJSE_HostObject* lpNewBinding);
+                               CFXJSE_HostObject* pNewBinding);
 
 void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hJSObject);
-CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Object> hJSObject);
+CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Value> hValue);
 
 #endif  // FXJS_XFA_CFXJSE_CONTEXT_H_
diff --git a/fxjs/xfa/cfxjse_engine.cpp b/fxjs/xfa/cfxjse_engine.cpp
index 9be2498..601ffb4 100644
--- a/fxjs/xfa/cfxjse_engine.cpp
+++ b/fxjs/xfa/cfxjse_engine.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,34 +9,38 @@
 #include <utility>
 
 #include "core/fxcrt/autorestorer.h"
-#include "core/fxcrt/cfx_widetextbuf.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxcrt/widetext_buffer.h"
 #include "fxjs/cjs_runtime.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_formcalc_context.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
+#include "fxjs/xfa/cfxjse_nodehelper.h"
 #include "fxjs/xfa/cfxjse_resolveprocessor.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodehelper.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 #include "xfa/fxfa/parser/cxfa_thisproxy.h"
-#include "xfa/fxfa/parser/cxfa_treelist.h"
+#include "xfa/fxfa/parser/cxfa_variables.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 using pdfium::fxjse::kClassTag;
 
-const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor = {
+const FXJSE_CLASS_DESCRIPTOR kGlobalClassDescriptor = {
     kClassTag,  // tag
     "Root",     // name
     nullptr,    // methods
@@ -47,7 +51,7 @@
     CFXJSE_Engine::NormalMethodCall,
 };
 
-const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor = {
+const FXJSE_CLASS_DESCRIPTOR kNormalClassDescriptor = {
     kClassTag,    // tag
     "XFAObject",  // name
     nullptr,      // methods
@@ -58,7 +62,7 @@
     CFXJSE_Engine::NormalMethodCall,
 };
 
-const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor = {
+const FXJSE_CLASS_DESCRIPTOR kVariablesClassDescriptor = {
     kClassTag,          // tag
     "XFAScriptObject",  // name
     nullptr,            // methods
@@ -73,28 +77,46 @@
 
 const char kFormCalcRuntime[] = "pfm_rt";
 
-CXFA_ThisProxy* ToThisProxy(CFXJSE_Value* pValue) {
-  CFXJSE_HostObject* pHostObject = pValue->ToHostObject();
-  return pHostObject ? ToThisProxy(pHostObject->AsCXFAObject()) : nullptr;
-}
-
 }  // namespace
 
+CFXJSE_Engine::ResolveResult::ResolveResult() = default;
+
+CFXJSE_Engine::ResolveResult::ResolveResult(const ResolveResult& that) =
+    default;
+
+CFXJSE_Engine::ResolveResult& CFXJSE_Engine::ResolveResult::operator=(
+    const ResolveResult& that) = default;
+
+CFXJSE_Engine::ResolveResult::~ResolveResult() = default;
+
 // static
 CXFA_Object* CFXJSE_Engine::ToObject(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
-  if (!info.Holder()->IsObject())
+  return ToObject(info.GetIsolate(), info.Holder());
+}
+
+// static
+CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate,
+                                     v8::Local<v8::Value> value) {
+  if (!value->IsObject())
     return nullptr;
 
-  CFXJSE_HostObject* pHostObj =
-      FXJSE_RetrieveObjectBinding(info.Holder().As<v8::Object>());
-  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+  return ToObject(FXJSE_RetrieveObjectBinding(value.As<v8::Object>()));
 }
 
 // static.
-CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_Value* pValue) {
-  CFXJSE_HostObject* pHostObj = pValue->ToHostObject();
-  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate,
+                                     CFXJSE_Value* pValue) {
+  return ToObject(pValue->ToHostObject(pIsolate));
+}
+
+// static
+CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_HostObject* pHostObj) {
+  if (!pHostObj)
+    return nullptr;
+
+  CJX_Object* pJSObject = pHostObj->AsCJXObject();
+  return pJSObject ? pJSObject->GetXFAObject() : nullptr;
 }
 
 CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument,
@@ -103,311 +125,352 @@
       m_pSubordinateRuntime(fxjs_runtime),
       m_pDocument(pDocument),
       m_JsContext(CFXJSE_Context::Create(fxjs_runtime->GetIsolate(),
-                                         &GlobalClassDescriptor,
-                                         pDocument->GetRoot())),
-      m_ResolveProcessor(pdfium::MakeUnique<CFXJSE_ResolveProcessor>()) {
+                                         &kGlobalClassDescriptor,
+                                         pDocument->GetRoot()->JSObject(),
+                                         nullptr)),
+      m_NodeHelper(std::make_unique<CFXJSE_NodeHelper>()),
+      m_ResolveProcessor(
+          std::make_unique<CFXJSE_ResolveProcessor>(this, m_NodeHelper.get())) {
   RemoveBuiltInObjs(m_JsContext.get());
   m_JsContext->EnableCompatibleMode();
 
   // Don't know if this can happen before we remove the builtin objs and set
   // compatibility mode.
   m_pJsClass =
-      CFXJSE_Class::Create(m_JsContext.get(), &NormalClassDescriptor, false);
+      CFXJSE_Class::Create(m_JsContext.get(), &kNormalClassDescriptor, false);
 }
 
 CFXJSE_Engine::~CFXJSE_Engine() {
-  for (const auto& pair : m_mapVariableToContext)
-    delete ToThisProxy(pair.second->GetGlobalObject().get());
+  // This is what ensures that the v8 object bound to a CJX_Object
+  // no longer retains that binding since it will outlive that object.
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+  for (const auto& pair : m_mapObjectToObject) {
+    const v8::Global<v8::Object>& binding = pair.second;
+    FXJSE_ClearObjectBinding(v8::Local<v8::Object>::New(GetIsolate(), binding));
+  }
+}
 
-  for (const auto& pair : m_mapObjectToValue)
-    pair.second->ClearHostObject();
+CFXJSE_Engine::EventParamScope::EventParamScope(CFXJSE_Engine* pEngine,
+                                                CXFA_Node* pTarget,
+                                                CXFA_EventParam* pEventParam)
+    : m_pEngine(pEngine),
+      m_pPrevTarget(pEngine->GetEventTarget()),
+      m_pPrevEventParam(pEngine->GetEventParam()) {
+  m_pEngine->m_pTarget = pTarget;
+  m_pEngine->m_eventParam = pEventParam;
+}
+
+CFXJSE_Engine::EventParamScope::~EventParamScope() {
+  m_pEngine->m_pTarget = m_pPrevTarget;
+  m_pEngine->m_eventParam = m_pPrevEventParam;
 }
 
 bool CFXJSE_Engine::RunScript(CXFA_Script::Type eScriptType,
                               WideStringView wsScript,
                               CFXJSE_Value* hRetValue,
                               CXFA_Object* pThisObject) {
-  ByteString btScript;
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
   AutoRestorer<CXFA_Script::Type> typeRestorer(&m_eScriptType);
   m_eScriptType = eScriptType;
+
+  ByteString btScript;
   if (eScriptType == CXFA_Script::Type::Formcalc) {
-    if (!m_FM2JSContext) {
-      m_FM2JSContext = pdfium::MakeUnique<CFXJSE_FormCalcContext>(
+    if (!m_FormCalcContext) {
+      m_FormCalcContext = std::make_unique<CFXJSE_FormCalcContext>(
           GetIsolate(), m_JsContext.get(), m_pDocument.Get());
     }
-    CFX_WideTextBuf wsJavaScript;
-    if (!CFXJSE_FormCalcContext::Translate(wsScript, &wsJavaScript)) {
-      hRetValue->SetUndefined();
+    absl::optional<WideTextBuffer> wsJavaScript =
+        CFXJSE_FormCalcContext::Translate(m_pDocument->GetHeap(), wsScript);
+    if (!wsJavaScript.has_value()) {
+      hRetValue->SetUndefined(GetIsolate());
       return false;
     }
-    btScript = FX_UTF8Encode(wsJavaScript.AsStringView());
+    btScript = FX_UTF8Encode(wsJavaScript.value().AsStringView());
   } else {
     btScript = FX_UTF8Encode(wsScript);
   }
-  AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
+  AutoRestorer<cppgc::Persistent<CXFA_Object>> nodeRestorer(&m_pThisObject);
   m_pThisObject = pThisObject;
 
-  CFXJSE_Value* pValue =
-      pThisObject ? GetOrCreateJSBindingFromMap(pThisObject) : nullptr;
-  IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime.Get());
-  return m_JsContext->ExecuteScript(btScript.c_str(), hRetValue, pValue);
+  v8::Local<v8::Object> pThisBinding;
+  if (pThisObject)
+    pThisBinding = GetOrCreateJSBindingFromMap(pThisObject);
+
+  IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime);
+  return m_JsContext->ExecuteScript(btScript.AsStringView(), hRetValue,
+                                    pThisBinding);
 }
 
 bool CFXJSE_Engine::QueryNodeByFlag(CXFA_Node* refNode,
                                     WideStringView propname,
-                                    CFXJSE_Value* pValue,
-                                    uint32_t dwFlag,
-                                    bool bSetting) {
+                                    v8::Local<v8::Value>* pValue,
+                                    Mask<XFA_ResolveFlag> dwFlag) {
   if (!refNode)
     return false;
 
-  XFA_RESOLVENODE_RS resolveRs;
-  if (!ResolveObjects(refNode, propname, &resolveRs, dwFlag, nullptr))
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      ResolveObjects(refNode, propname, dwFlag);
+  if (!maybeResult.has_value())
     return false;
-  if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    pValue->Assign(
-        GetOrCreateJSBindingFromMap(resolveRs.objects.front().Get()));
+
+  if (maybeResult.value().type == ResolveResult::Type::kNodes) {
+    *pValue =
+        GetOrCreateJSBindingFromMap(maybeResult.value().objects.front().Get());
     return true;
   }
-  if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Attribute &&
-      resolveRs.script_attribute.callback) {
-    CJX_Object* jsObject = resolveRs.objects.front()->JSObject();
-    (*resolveRs.script_attribute.callback)(
-        jsObject, pValue, bSetting, resolveRs.script_attribute.attribute);
+  if (maybeResult.value().type == ResolveResult::Type::kAttribute &&
+      maybeResult.value().script_attribute.callback) {
+    CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
+    (*maybeResult.value().script_attribute.callback)(
+        GetIsolate(), jsObject, pValue, false,
+        maybeResult.value().script_attribute.attribute);
+  }
+  return true;
+}
+
+bool CFXJSE_Engine::UpdateNodeByFlag(CXFA_Node* refNode,
+                                     WideStringView propname,
+                                     v8::Local<v8::Value> pValue,
+                                     Mask<XFA_ResolveFlag> dwFlag) {
+  if (!refNode)
+    return false;
+
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      ResolveObjects(refNode, propname, dwFlag);
+  if (!maybeResult.has_value())
+    return false;
+
+  if (maybeResult.value().type == ResolveResult::Type::kAttribute &&
+      maybeResult.value().script_attribute.callback) {
+    CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
+    (*maybeResult.value().script_attribute.callback)(
+        GetIsolate(), jsObject, &pValue, true,
+        maybeResult.value().script_attribute.attribute);
   }
   return true;
 }
 
 // static
-void CFXJSE_Engine::GlobalPropertySetter(CFXJSE_Value* pObject,
+void CFXJSE_Engine::GlobalPropertySetter(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pObject,
                                          ByteStringView szPropName,
-                                         CFXJSE_Value* pValue) {
-  CXFA_Object* lpOrginalNode = ToObject(pObject);
-  CXFA_Document* pDoc = lpOrginalNode->GetDocument();
-  CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
-  CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
-  if (lpOrginalNode->IsThisProxy())
-    pRefNode = ToNode(lpScriptContext->GetVariablesThis(lpOrginalNode, false));
+                                         v8::Local<v8::Value> pValue) {
+  CXFA_Object* pOriginalNode = ToObject(pIsolate, pObject);
+  CXFA_Document* pDoc = pOriginalNode->GetDocument();
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  CXFA_Node* pRefNode = ToNode(pScriptContext->GetThisObject());
+  if (pOriginalNode->IsThisProxy())
+    pRefNode = ToNode(pScriptContext->GetVariablesThis(pOriginalNode));
 
   WideString wsPropName = WideString::FromUTF8(szPropName);
-  if (lpScriptContext->QueryNodeByFlag(
+  if (pScriptContext->UpdateNodeByFlag(
           pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings |
-              XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-              XFA_RESOLVENODE_Attributes,
-          true)) {
+          Mask<XFA_ResolveFlag>{
+              XFA_ResolveFlag::kParent, XFA_ResolveFlag::kSiblings,
+              XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
+              XFA_ResolveFlag::kAttributes})) {
     return;
   }
-  if (lpOrginalNode->IsThisProxy() && pValue && pValue->IsUndefined()) {
-    pObject->DeleteObjectProperty(szPropName);
+  if (pOriginalNode->IsThisProxy() && fxv8::IsUndefined(pValue)) {
+    fxv8::ReentrantDeleteObjectPropertyHelper(pScriptContext->GetIsolate(),
+                                              pObject, szPropName);
     return;
   }
   CXFA_FFNotify* pNotify = pDoc->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  auto* pCJSRuntime =
-      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
   if (!pCJSRuntime)
     return;
 
-  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
-  pCJSRuntime->SetValueByNameInGlobalObject(
-      szPropName, v8::Local<v8::Value>::New(lpScriptContext->GetIsolate(),
-                                            pValue->DirectGetValue()));
+  pCJSRuntime->SetValueByNameInGlobalObject(szPropName, pValue);
 }
 
 // static
-void CFXJSE_Engine::GlobalPropertyGetter(CFXJSE_Value* pObject,
-                                         ByteStringView szPropName,
-                                         CFXJSE_Value* pValue) {
-  CXFA_Object* pOriginalObject = ToObject(pObject);
+v8::Local<v8::Value> CFXJSE_Engine::GlobalPropertyGetter(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pObject,
+    ByteStringView szPropName) {
+  CXFA_Object* pOriginalObject = ToObject(pIsolate, pObject);
   CXFA_Document* pDoc = pOriginalObject->GetDocument();
-  CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
   WideString wsPropName = WideString::FromUTF8(szPropName);
 
-  pValue->SetUndefined();  // Assume failure.
-  if (lpScriptContext->GetType() == CXFA_Script::Type::Formcalc) {
-    if (szPropName == kFormCalcRuntime) {
-      lpScriptContext->m_FM2JSContext->GlobalPropertyGetter(pValue);
-      return;
-    }
-    XFA_HashCode uHashCode = static_cast<XFA_HashCode>(
-        FX_HashCode_GetW(wsPropName.AsStringView(), false));
+  // Assume failure.
+  v8::Local<v8::Value> pValue = fxv8::NewUndefinedHelper(pIsolate);
+
+  if (pScriptContext->GetType() == CXFA_Script::Type::Formcalc) {
+    if (szPropName == kFormCalcRuntime)
+      return pScriptContext->m_FormCalcContext->GlobalPropertyGetter();
+
+    XFA_HashCode uHashCode =
+        static_cast<XFA_HashCode>(FX_HashCode_GetW(wsPropName.AsStringView()));
     if (uHashCode != XFA_HASHCODE_Layout) {
       CXFA_Object* pObj =
-          lpScriptContext->GetDocument()->GetXFAObject(uHashCode);
-      if (pObj) {
-        pValue->Assign(lpScriptContext->GetOrCreateJSBindingFromMap(pObj));
-        return;
-      }
+          pScriptContext->GetDocument()->GetXFAObject(uHashCode);
+      if (pObj)
+        return pScriptContext->GetOrCreateJSBindingFromMap(pObj);
     }
   }
 
-  CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
-  if (pOriginalObject->IsThisProxy()) {
-    pRefNode =
-        ToNode(lpScriptContext->GetVariablesThis(pOriginalObject, false));
+  CXFA_Node* pRefNode = ToNode(pScriptContext->GetThisObject());
+  if (pOriginalObject->IsThisProxy())
+    pRefNode = ToNode(pScriptContext->GetVariablesThis(pOriginalObject));
+
+  if (pScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), &pValue,
+          Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
+                                XFA_ResolveFlag::kProperties,
+                                XFA_ResolveFlag::kAttributes})) {
+    return pValue;
   }
-  if (lpScriptContext->QueryNodeByFlag(
-          pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-              XFA_RESOLVENODE_Attributes,
-          false)) {
-    return;
-  }
-  if (lpScriptContext->QueryNodeByFlag(
-          pRefNode, wsPropName.AsStringView(), pValue,
-          XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false)) {
-    return;
+  if (pScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), &pValue,
+          Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
+                                XFA_ResolveFlag::kSiblings})) {
+    return pValue;
   }
 
   CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
-  if (pScriptObject && lpScriptContext->QueryVariableValue(
-                           pScriptObject->AsNode(), szPropName, pValue, true)) {
-    return;
+      pScriptContext->GetVariablesScript(pOriginalObject);
+  if (pScriptObject && pScriptContext->QueryVariableValue(
+                           CXFA_Script::FromNode(pScriptObject->AsNode()),
+                           szPropName, &pValue)) {
+    return pValue;
   }
 
   CXFA_FFNotify* pNotify = pDoc->GetNotify();
   if (!pNotify)
-    return;
+    return pValue;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  auto* pCJSRuntime =
-      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
   if (!pCJSRuntime)
-    return;
+    return pValue;
 
-  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
-  v8::Local<v8::Value> temp_value;
-  if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_value))
-    return;
+  v8::Local<v8::Value> temp_value =
+      pCJSRuntime->GetValueByNameFromGlobalObject(szPropName);
 
-  if (temp_value.IsEmpty())
-    return;
-
-  pValue->ForceSetValue(temp_value);
-}
-
-int32_t CFXJSE_Engine::GlobalPropTypeGetter(CFXJSE_Value* pOriginalValue,
-                                            ByteStringView szPropName,
-                                            bool bQueryIn) {
-  CXFA_Object* pObject = ToObject(pOriginalValue);
-  if (!pObject)
-    return FXJSE_ClassPropType_None;
-
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject, false);
-  WideString wsPropName = WideString::FromUTF8(szPropName);
-  if (pObject->JSObject()->HasMethod(wsPropName))
-    return FXJSE_ClassPropType_Method;
-
-  return FXJSE_ClassPropType_Property;
+  return !temp_value.IsEmpty() ? temp_value : pValue;
 }
 
 // static
-void CFXJSE_Engine::NormalPropertyGetter(CFXJSE_Value* pOriginalValue,
-                                         ByteStringView szPropName,
-                                         CFXJSE_Value* pReturnValue) {
-  pReturnValue->SetUndefined();  // Assume failure.
-  CXFA_Object* pOriginalObject = ToObject(pOriginalValue);
+FXJSE_ClassPropType CFXJSE_Engine::GlobalPropTypeGetter(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pHolder,
+    ByteStringView szPropName,
+    bool bQueryIn) {
+  CXFA_Object* pObject = ToObject(pIsolate, pHolder);
+  if (!pObject)
+    return FXJSE_ClassPropType::kNone;
+
+  CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = pScriptContext->GetVariablesThis(pObject);
+  WideString wsPropName = WideString::FromUTF8(szPropName);
+  if (pObject->JSObject()->HasMethod(wsPropName))
+    return FXJSE_ClassPropType::kMethod;
+
+  return FXJSE_ClassPropType::kProperty;
+}
+
+// static
+v8::Local<v8::Value> CFXJSE_Engine::NormalPropertyGetter(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pHolder,
+    ByteStringView szPropName) {
+  CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder);
   if (!pOriginalObject)
-    return;
+    return fxv8::NewUndefinedHelper(pIsolate);
+
+  CFXJSE_Engine* pScriptContext =
+      pOriginalObject->GetDocument()->GetScriptContext();
 
   WideString wsPropName = WideString::FromUTF8(szPropName);
-  CFXJSE_Engine* lpScriptContext =
-      pOriginalObject->GetDocument()->GetScriptContext();
-  CXFA_Object* pObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, false);
   if (wsPropName.EqualsASCII("xfa")) {
-    CFXJSE_Value* pValue = lpScriptContext->GetOrCreateJSBindingFromMap(
-        lpScriptContext->GetDocument()->GetRoot());
-    pReturnValue->Assign(pValue);
-    return;
+    return pScriptContext->GetOrCreateJSBindingFromMap(
+        pScriptContext->GetDocument()->GetRoot());
   }
 
-  bool bRet = lpScriptContext->QueryNodeByFlag(
-      ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
-      XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-          XFA_RESOLVENODE_Attributes,
-      false);
-  if (bRet)
-    return;
-
-  if (pObject == lpScriptContext->GetThisObject() ||
-      (lpScriptContext->GetType() == CXFA_Script::Type::Javascript &&
-       !lpScriptContext->IsStrictScopeInJavaScript())) {
-    bRet = lpScriptContext->QueryNodeByFlag(
-        ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
-        XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false);
+  v8::Local<v8::Value> pReturnValue = fxv8::NewUndefinedHelper(pIsolate);
+  CXFA_Object* pObject = pScriptContext->GetVariablesThis(pOriginalObject);
+  CXFA_Node* pRefNode = ToNode(pObject);
+  if (pScriptContext->QueryNodeByFlag(
+          pRefNode, wsPropName.AsStringView(), &pReturnValue,
+          Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
+                                XFA_ResolveFlag::kProperties,
+                                XFA_ResolveFlag::kAttributes})) {
+    return pReturnValue;
   }
-  if (bRet)
-    return;
-
+  if (pObject == pScriptContext->GetThisObject() ||
+      (pScriptContext->GetType() == CXFA_Script::Type::Javascript &&
+       !pScriptContext->IsStrictScopeInJavaScript())) {
+    if (pScriptContext->QueryNodeByFlag(
+            pRefNode, wsPropName.AsStringView(), &pReturnValue,
+            Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
+                                  XFA_ResolveFlag::kSiblings})) {
+      return pReturnValue;
+    }
+  }
   CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
+      pScriptContext->GetVariablesScript(pOriginalObject);
   if (!pScriptObject)
-    return;
+    return pReturnValue;
 
-  bRet = lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
-                                             pReturnValue, true);
-  if (bRet)
-    return;
-
-  Optional<XFA_SCRIPTATTRIBUTEINFO> info = XFA_GetScriptAttributeByName(
+  if (pScriptContext->QueryVariableValue(
+          CXFA_Script::FromNode(pScriptObject->AsNode()), szPropName,
+          &pReturnValue)) {
+    return pReturnValue;
+  }
+  absl::optional<XFA_SCRIPTATTRIBUTEINFO> info = XFA_GetScriptAttributeByName(
       pObject->GetElementType(), wsPropName.AsStringView());
   if (info.has_value()) {
-    CJX_Object* jsObject = pObject->JSObject();
-    (*info.value().callback)(jsObject, pReturnValue, false,
-                             info.value().attribute);
-    return;
+    (*info.value().callback)(pIsolate, pObject->JSObject(), &pReturnValue,
+                             false, info.value().attribute);
+    return pReturnValue;
   }
 
   CXFA_FFNotify* pNotify = pObject->GetDocument()->GetNotify();
   if (!pNotify)
-    return;
+    return pReturnValue;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  auto* pCJSRuntime =
-      static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
   if (!pCJSRuntime)
-    return;
+    return pReturnValue;
 
-  v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
-  v8::Local<v8::Value> temp_local;
-  if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_local))
-    return;
+  v8::Local<v8::Value> temp_local =
+      pCJSRuntime->GetValueByNameFromGlobalObject(szPropName);
 
-  if (temp_local.IsEmpty())
-    return;
-
-  pReturnValue->ForceSetValue(temp_local);
+  return !temp_local.IsEmpty() ? temp_local : pReturnValue;
 }
 
 // static
-void CFXJSE_Engine::NormalPropertySetter(CFXJSE_Value* pOriginalValue,
+void CFXJSE_Engine::NormalPropertySetter(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Object> pHolder,
                                          ByteStringView szPropName,
-                                         CFXJSE_Value* pReturnValue) {
-  CXFA_Object* pOriginalObject = ToObject(pOriginalValue);
+                                         v8::Local<v8::Value> pValue) {
+  CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder);
   if (!pOriginalObject)
     return;
 
-  CFXJSE_Engine* lpScriptContext =
+  CFXJSE_Engine* pScriptContext =
       pOriginalObject->GetDocument()->GetScriptContext();
-  CXFA_Object* pObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, false);
+  if (pScriptContext->IsResolvingNodes())
+    return;
+
+  CXFA_Object* pObject = pScriptContext->GetVariablesThis(pOriginalObject);
   WideString wsPropName = WideString::FromUTF8(szPropName);
   WideStringView wsPropNameView = wsPropName.AsStringView();
-  Optional<XFA_SCRIPTATTRIBUTEINFO> info =
+  absl::optional<XFA_SCRIPTATTRIBUTEINFO> info =
       XFA_GetScriptAttributeByName(pObject->GetElementType(), wsPropNameView);
   if (info.has_value()) {
     CJX_Object* jsObject = pObject->JSObject();
-    (*info.value().callback)(jsObject, pReturnValue, true,
+    (*info.value().callback)(pIsolate, jsObject, &pValue, true,
                              info.value().attribute);
     return;
   }
@@ -430,7 +493,7 @@
       info = XFA_GetScriptAttributeByName(pPropOrChild->GetElementType(),
                                           L"{default}");
       if (info.has_value()) {
-        pPropOrChild->JSObject()->ScriptSomDefaultValue(pReturnValue, true,
+        pPropOrChild->JSObject()->ScriptSomDefaultValue(pIsolate, &pValue, true,
                                                         XFA_Attribute::Unknown);
         return;
       }
@@ -438,32 +501,36 @@
   }
 
   CXFA_Object* pScriptObject =
-      lpScriptContext->GetVariablesThis(pOriginalObject, true);
+      pScriptContext->GetVariablesScript(pOriginalObject);
   if (pScriptObject) {
-    lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
-                                        pReturnValue, false);
+    pScriptContext->UpdateVariableValue(
+        CXFA_Script::FromNode(pScriptObject->AsNode()), szPropName, pValue);
   }
 }
 
-int32_t CFXJSE_Engine::NormalPropTypeGetter(CFXJSE_Value* pOriginalValue,
-                                            ByteStringView szPropName,
-                                            bool bQueryIn) {
-  CXFA_Object* pObject = ToObject(pOriginalValue);
+FXJSE_ClassPropType CFXJSE_Engine::NormalPropTypeGetter(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Object> pHolder,
+    ByteStringView szPropName,
+    bool bQueryIn) {
+  CXFA_Object* pObject = ToObject(pIsolate, pHolder);
   if (!pObject)
-    return FXJSE_ClassPropType_None;
+    return FXJSE_ClassPropType::kNone;
 
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject, false);
+  CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = pScriptContext->GetVariablesThis(pObject);
   XFA_Element eType = pObject->GetElementType();
   WideString wsPropName = WideString::FromUTF8(szPropName);
   if (pObject->JSObject()->HasMethod(wsPropName))
-    return FXJSE_ClassPropType_Method;
+    return FXJSE_ClassPropType::kMethod;
 
-  if (bQueryIn &&
-      !XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView())) {
-    return FXJSE_ClassPropType_None;
+  if (bQueryIn) {
+    absl::optional<XFA_SCRIPTATTRIBUTEINFO> maybe_info =
+        XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView());
+    if (!maybe_info.has_value())
+      return FXJSE_ClassPropType::kNone;
   }
-  return FXJSE_ClassPropType_Property;
+  return FXJSE_ClassPropType::kProperty;
 }
 
 CJS_Result CFXJSE_Engine::NormalMethodCall(
@@ -473,8 +540,8 @@
   if (!pObject)
     return CJS_Result::Failure(L"no Holder() present.");
 
-  CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
-  pObject = lpScriptContext->GetVariablesThis(pObject, false);
+  CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
+  pObject = pScriptContext->GetVariablesThis(pObject);
 
   std::vector<v8::Local<v8::Value>> parameters;
   for (int i = 0; i < info.Length(); i++)
@@ -491,154 +558,184 @@
   return m_eScriptType;
 }
 
-CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Node* pScriptNode,
+void CFXJSE_Engine::AddObjectToUpArray(CXFA_Node* pNode) {
+  m_upObjectArray.push_back(pNode);
+}
+
+CXFA_Node* CFXJSE_Engine::LastObjectFromUpArray() {
+  return !m_upObjectArray.empty() ? m_upObjectArray.back() : nullptr;
+}
+
+CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Script* pScriptNode,
                                                       CXFA_Node* pSubform) {
   if (!pScriptNode || !pSubform)
     return nullptr;
 
-  auto pNewContext =
-      CFXJSE_Context::Create(GetIsolate(), &VariablesClassDescriptor,
-                             new CXFA_ThisProxy(pSubform, pScriptNode));
+  auto* proxy = cppgc::MakeGarbageCollected<CXFA_ThisProxy>(
+      pScriptNode->GetDocument()->GetHeap()->GetAllocationHandle(), pSubform,
+      pScriptNode);
+  auto pNewContext = CFXJSE_Context::Create(
+      GetIsolate(), &kVariablesClassDescriptor, proxy->JSObject(), proxy);
   RemoveBuiltInObjs(pNewContext.get());
   pNewContext->EnableCompatibleMode();
   CFXJSE_Context* pResult = pNewContext.get();
-  m_mapVariableToContext[pScriptNode] = std::move(pNewContext);
+  m_mapVariableToContext[pScriptNode->JSObject()] = std::move(pNewContext);
   return pResult;
 }
 
-CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject,
-                                             bool bScriptNode) {
+CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject) {
   CXFA_ThisProxy* pProxy = ToThisProxy(pObject);
-  if (!pProxy)
-    return pObject;
-
-  return bScriptNode ? pProxy->GetScriptNode() : pProxy->GetThisNode();
+  return pProxy ? pProxy->GetThisNode() : pObject;
 }
 
-bool CFXJSE_Engine::RunVariablesScript(CXFA_Node* pScriptNode) {
+CXFA_Object* CFXJSE_Engine::GetVariablesScript(CXFA_Object* pObject) {
+  CXFA_ThisProxy* pProxy = ToThisProxy(pObject);
+  return pProxy ? pProxy->GetScriptNode() : pObject;
+}
+
+void CFXJSE_Engine::RunVariablesScript(CXFA_Script* pScriptNode) {
   if (!pScriptNode)
-    return false;
+    return;
 
-  if (pScriptNode->GetElementType() != XFA_Element::Script)
-    return true;
+  auto* pParent = CXFA_Variables::FromNode(pScriptNode->GetParent());
+  if (!pParent)
+    return;
 
-  CXFA_Node* pParent = pScriptNode->GetParent();
-  if (!pParent || pParent->GetElementType() != XFA_Element::Variables)
-    return false;
-
-  auto it = m_mapVariableToContext.find(pScriptNode);
+  auto it = m_mapVariableToContext.find(pScriptNode->JSObject());
   if (it != m_mapVariableToContext.end() && it->second)
-    return true;
+    return;
 
   CXFA_Node* pTextNode = pScriptNode->GetFirstChild();
   if (!pTextNode)
-    return false;
+    return;
 
-  Optional<WideString> wsScript =
+  absl::optional<WideString> wsScript =
       pTextNode->JSObject()->TryCData(XFA_Attribute::Value, true);
-  if (!wsScript)
-    return false;
+  if (!wsScript.has_value())
+    return;
 
   ByteString btScript = wsScript->ToUTF8();
-  auto hRetValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  auto hRetValue = std::make_unique<CFXJSE_Value>();
   CXFA_Node* pThisObject = pParent->GetParent();
   CFXJSE_Context* pVariablesContext =
       CreateVariablesContext(pScriptNode, pThisObject);
-  AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
+  AutoRestorer<cppgc::Persistent<CXFA_Object>> nodeRestorer(&m_pThisObject);
   m_pThisObject = pThisObject;
-  return pVariablesContext->ExecuteScript(btScript.c_str(), hRetValue.get(),
-                                          nullptr);
+  pVariablesContext->ExecuteScript(btScript.AsStringView(), hRetValue.get(),
+                                   v8::Local<v8::Object>());
 }
 
-bool CFXJSE_Engine::QueryVariableValue(CXFA_Node* pScriptNode,
+CFXJSE_Context* CFXJSE_Engine::VariablesContextForScriptNode(
+    CXFA_Script* pScriptNode) {
+  if (!pScriptNode)
+    return nullptr;
+
+  auto* variablesNode = CXFA_Variables::FromNode(pScriptNode->GetParent());
+  if (!variablesNode)
+    return nullptr;
+
+  auto it = m_mapVariableToContext.find(pScriptNode->JSObject());
+  return it != m_mapVariableToContext.end() ? it->second.get() : nullptr;
+}
+
+bool CFXJSE_Engine::QueryVariableValue(CXFA_Script* pScriptNode,
                                        ByteStringView szPropName,
-                                       CFXJSE_Value* pValue,
-                                       bool bGetter) {
-  if (!pScriptNode || pScriptNode->GetElementType() != XFA_Element::Script)
+                                       v8::Local<v8::Value>* pValue) {
+  CFXJSE_Context* pVariableContext = VariablesContextForScriptNode(pScriptNode);
+  if (!pVariableContext)
     return false;
 
-  CXFA_Node* variablesNode = pScriptNode->GetParent();
-  if (!variablesNode ||
-      variablesNode->GetElementType() != XFA_Element::Variables)
+  v8::Local<v8::Object> pObject = pVariableContext->GetGlobalObject();
+  if (!fxv8::ReentrantHasObjectOwnPropertyHelper(GetIsolate(), pObject,
+                                                 szPropName)) {
     return false;
-
-  auto it = m_mapVariableToContext.find(pScriptNode);
-  if (it == m_mapVariableToContext.end() || !it->second)
-    return false;
-
-  CFXJSE_Context* pVariableContext = it->second.get();
-  std::unique_ptr<CFXJSE_Value> pObject = pVariableContext->GetGlobalObject();
-  auto hVariableValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  if (!bGetter) {
-    pObject->SetObjectOwnProperty(szPropName, pValue);
-    return true;
   }
 
-  if (!pObject->HasObjectOwnProperty(szPropName, false))
-    return false;
-
-  pObject->GetObjectProperty(szPropName, hVariableValue.get());
-  if (hVariableValue->IsFunction())
-    pValue->SetFunctionBind(hVariableValue.get(), pObject.get());
-  else if (bGetter)
-    pValue->Assign(hVariableValue.get());
-  else
-    hVariableValue.get()->Assign(pValue);
+  v8::Local<v8::Value> hVariableValue =
+      fxv8::ReentrantGetObjectPropertyHelper(GetIsolate(), pObject, szPropName);
+  if (fxv8::IsFunction(hVariableValue)) {
+    v8::Local<v8::Function> maybeFunc = CFXJSE_Value::NewBoundFunction(
+        GetIsolate(), hVariableValue.As<v8::Function>(), pObject);
+    if (!maybeFunc.IsEmpty())
+      *pValue = maybeFunc;
+  } else {
+    *pValue = hVariableValue;
+  }
   return true;
 }
 
-void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) const {
-  const ByteStringView kObjNames[2] = {"Number", "Date"};
-  std::unique_ptr<CFXJSE_Value> pObject = pContext->GetGlobalObject();
-  auto hProp = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  for (const auto& obj : kObjNames) {
-    if (pObject->GetObjectProperty(obj, hProp.get()))
-      pObject->DeleteObjectProperty(obj);
-  }
-}
-
-bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject,
-                                   WideStringView wsExpression,
-                                   XFA_RESOLVENODE_RS* resolveNodeRS,
-                                   uint32_t dwStyles,
-                                   CXFA_Node* bindNode) {
-  if (wsExpression.IsEmpty())
+bool CFXJSE_Engine::UpdateVariableValue(CXFA_Script* pScriptNode,
+                                        ByteStringView szPropName,
+                                        v8::Local<v8::Value> pValue) {
+  CFXJSE_Context* pVariableContext = VariablesContextForScriptNode(pScriptNode);
+  if (!pVariableContext)
     return false;
 
-  if (m_eScriptType != CXFA_Script::Type::Formcalc ||
-      (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
+  v8::Local<v8::Object> pObject = pVariableContext->GetGlobalObject();
+  fxv8::ReentrantSetObjectOwnPropertyHelper(GetIsolate(), pObject, szPropName,
+                                            pValue);
+  return true;
+}
+
+void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) {
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+  v8::Local<v8::Object> pObject = pContext->GetGlobalObject();
+  fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Number");
+  fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Date");
+}
+
+absl::optional<CFXJSE_Engine::ResolveResult> CFXJSE_Engine::ResolveObjects(
+    CXFA_Object* refObject,
+    WideStringView wsExpression,
+    Mask<XFA_ResolveFlag> dwStyles) {
+  return ResolveObjectsWithBindNode(refObject, wsExpression, dwStyles, nullptr);
+}
+
+absl::optional<CFXJSE_Engine::ResolveResult>
+CFXJSE_Engine::ResolveObjectsWithBindNode(CXFA_Object* refObject,
+                                          WideStringView wsExpression,
+                                          Mask<XFA_ResolveFlag> dwStyles,
+                                          CXFA_Node* bindNode) {
+  if (wsExpression.IsEmpty())
+    return absl::nullopt;
+
+  AutoRestorer<bool> resolving_restorer(&m_bResolvingNodes);
+  m_bResolvingNodes = true;
+
+  const bool bParentOrSiblings =
+      !!(dwStyles & Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
+                                          XFA_ResolveFlag::kSiblings});
+  if (m_eScriptType != CXFA_Script::Type::Formcalc || bParentOrSiblings)
     m_upObjectArray.clear();
-  }
-  if (refObject && refObject->IsNode() &&
-      (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
+  if (refObject && refObject->IsNode() && bParentOrSiblings)
     m_upObjectArray.push_back(refObject->AsNode());
-  }
 
+  ResolveResult result;
   bool bNextCreate = false;
-  CXFA_NodeHelper* pNodeHelper = m_ResolveProcessor->GetNodeHelper();
-  if (dwStyles & XFA_RESOLVENODE_CreateNode)
-    pNodeHelper->SetCreateNodeType(bindNode);
+  if (dwStyles & XFA_ResolveFlag::kCreateNode)
+    m_NodeHelper->SetCreateNodeType(bindNode);
 
-  pNodeHelper->m_pCreateParent = nullptr;
-  pNodeHelper->m_iCurAllStart = -1;
+  m_NodeHelper->m_pCreateParent = nullptr;
+  m_NodeHelper->m_iCurAllStart = -1;
 
-  CFXJSE_ResolveNodeData rndFind(this);
+  CFXJSE_ResolveProcessor::NodeData rndFind;
   int32_t nStart = 0;
   int32_t nLevel = 0;
 
-  std::vector<UnownedPtr<CXFA_Object>> findObjects;
+  std::vector<cppgc::Member<CXFA_Object>> findObjects;
   findObjects.emplace_back(refObject ? refObject : m_pDocument->GetRoot());
   int32_t nNodes = 0;
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
   while (true) {
-    nNodes = pdfium::CollectionSize<int32_t>(findObjects);
+    nNodes = fxcrt::CollectionSize<int32_t>(findObjects);
     int32_t i = 0;
     rndFind.m_dwStyles = dwStyles;
     m_ResolveProcessor->SetCurStart(nStart);
     nStart = m_ResolveProcessor->GetFilter(wsExpression, nStart, rndFind);
     if (nStart < 1) {
-      if ((dwStyles & XFA_RESOLVENODE_CreateNode) && !bNextCreate) {
+      if ((dwStyles & XFA_ResolveFlag::kCreateNode) && !bNextCreate) {
         CXFA_Node* pDataNode = nullptr;
-        nStart = pNodeHelper->m_iCurAllStart;
+        nStart = m_NodeHelper->m_iCurAllStart;
         if (nStart != -1) {
           pDataNode = m_pDocument->GetNotBindNode(findObjects);
           if (pDataNode) {
@@ -652,9 +749,9 @@
           findObjects.emplace_back(pDataNode);
           break;
         }
-        dwStyles |= XFA_RESOLVENODE_Bind;
+        dwStyles |= XFA_ResolveFlag::kBind;
         findObjects.clear();
-        findObjects.emplace_back(pNodeHelper->m_pAllStartParent.Get());
+        findObjects.emplace_back(m_NodeHelper->m_pAllStartParent.Get());
         continue;
       }
       break;
@@ -662,62 +759,64 @@
     if (bNextCreate) {
       int32_t checked_length =
           pdfium::base::checked_cast<int32_t>(wsExpression.GetLength());
-      if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
-                                  nStart == checked_length, this)) {
+      if (m_NodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
+                                   nStart == checked_length, this)) {
         continue;
       }
       break;
     }
-    std::vector<UnownedPtr<CXFA_Object>> retObjects;
+    std::vector<cppgc::Member<CXFA_Object>> retObjects;
     while (i < nNodes) {
       bool bDataBind = false;
-      if (((dwStyles & XFA_RESOLVENODE_Bind) ||
-           (dwStyles & XFA_RESOLVENODE_CreateNode)) &&
+      if (((dwStyles & XFA_ResolveFlag::kBind) ||
+           (dwStyles & XFA_ResolveFlag::kCreateNode)) &&
           nNodes > 1) {
-        CFXJSE_ResolveNodeData rndBind(nullptr);
+        CFXJSE_ResolveProcessor::NodeData rndBind;
         m_ResolveProcessor->GetFilter(wsExpression, nStart, rndBind);
-        m_ResolveProcessor->SetIndexDataBind(rndBind.m_wsCondition, i, nNodes);
+        i = m_ResolveProcessor->IndexForDataBind(rndBind.m_wsCondition, nNodes);
         bDataBind = true;
       }
       rndFind.m_CurObject = findObjects[i++].Get();
       rndFind.m_nLevel = nLevel;
-      rndFind.m_dwFlag = XFA_ResolveNode_RSType_Nodes;
-      if (!m_ResolveProcessor->Resolve(rndFind))
+      rndFind.m_Result.type = ResolveResult::Type::kNodes;
+      if (!m_ResolveProcessor->Resolve(GetIsolate(), rndFind))
         continue;
 
-      if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute &&
-          rndFind.m_ScriptAttribute.callback &&
+      if (rndFind.m_Result.type == ResolveResult::Type::kAttribute &&
+          rndFind.m_Result.script_attribute.callback &&
           nStart <
               pdfium::base::checked_cast<int32_t>(wsExpression.GetLength())) {
-        auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-        CJX_Object* jsObject = rndFind.m_Objects.front()->JSObject();
-        (*rndFind.m_ScriptAttribute.callback)(
-            jsObject, pValue.get(), false, rndFind.m_ScriptAttribute.attribute);
-        if (!pValue->IsEmpty())
-          rndFind.m_Objects.front() = ToObject(pValue.get());
+        v8::Local<v8::Value> pValue;
+        CJX_Object* jsObject = rndFind.m_Result.objects.front()->JSObject();
+        (*rndFind.m_Result.script_attribute.callback)(
+            GetIsolate(), jsObject, &pValue, false,
+            rndFind.m_Result.script_attribute.attribute);
+        if (!pValue.IsEmpty()) {
+          rndFind.m_Result.objects.front() = ToObject(GetIsolate(), pValue);
+        }
       }
       if (!m_upObjectArray.empty())
         m_upObjectArray.pop_back();
-      retObjects.insert(retObjects.end(), rndFind.m_Objects.begin(),
-                        rndFind.m_Objects.end());
-      rndFind.m_Objects.clear();
+      retObjects.insert(retObjects.end(), rndFind.m_Result.objects.begin(),
+                        rndFind.m_Result.objects.end());
+      rndFind.m_Result.objects.clear();
       if (bDataBind)
         break;
     }
     findObjects.clear();
 
-    nNodes = pdfium::CollectionSize<int32_t>(retObjects);
+    nNodes = fxcrt::CollectionSize<int32_t>(retObjects);
     if (nNodes < 1) {
-      if (dwStyles & XFA_RESOLVENODE_CreateNode) {
+      if (dwStyles & XFA_ResolveFlag::kCreateNode) {
         bNextCreate = true;
-        if (!pNodeHelper->m_pCreateParent) {
-          pNodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject.Get());
-          pNodeHelper->m_iCreateCount = 1;
+        if (!m_NodeHelper->m_pCreateParent) {
+          m_NodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject);
+          m_NodeHelper->m_iCreateCount = 1;
         }
         int32_t checked_length =
             pdfium::base::checked_cast<int32_t>(wsExpression.GetLength());
-        if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
-                                    nStart == checked_length, this)) {
+        if (m_NodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
+                                     nStart == checked_length, this)) {
           continue;
         }
       }
@@ -725,101 +824,95 @@
     }
 
     findObjects = std::move(retObjects);
-    rndFind.m_Objects.clear();
-    if (nLevel == 0)
-      dwStyles &= ~(XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
-
+    rndFind.m_Result.objects.clear();
+    if (nLevel == 0) {
+      dwStyles.Clear(XFA_ResolveFlag::kParent);
+      dwStyles.Clear(XFA_ResolveFlag::kSiblings);
+    }
     nLevel++;
   }
 
   if (!bNextCreate) {
-    resolveNodeRS->dwFlags = rndFind.m_dwFlag;
+    result.type = rndFind.m_Result.type;
     if (nNodes > 0) {
-      resolveNodeRS->objects.insert(resolveNodeRS->objects.end(),
-                                    findObjects.begin(), findObjects.end());
+      result.objects.insert(result.objects.end(), findObjects.begin(),
+                            findObjects.end());
     }
-    if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute) {
-      resolveNodeRS->script_attribute = rndFind.m_ScriptAttribute;
-      return true;
+    if (rndFind.m_Result.type == ResolveResult::Type::kAttribute) {
+      result.script_attribute = rndFind.m_Result.script_attribute;
+      return result;
     }
   }
-  if (dwStyles & (XFA_RESOLVENODE_CreateNode | XFA_RESOLVENODE_Bind |
-                  XFA_RESOLVENODE_BindNew)) {
-    if (pNodeHelper->m_pCreateParent)
-      resolveNodeRS->objects.emplace_back(pNodeHelper->m_pCreateParent.Get());
+  if ((dwStyles & XFA_ResolveFlag::kCreateNode) ||
+      (dwStyles & XFA_ResolveFlag::kBind) ||
+      (dwStyles & XFA_ResolveFlag::kBindNew)) {
+    if (m_NodeHelper->m_pCreateParent)
+      result.objects.emplace_back(m_NodeHelper->m_pCreateParent.Get());
     else
-      pNodeHelper->CreateNodeForCondition(rndFind.m_wsCondition);
+      m_NodeHelper->CreateNodeForCondition(rndFind.m_wsCondition);
 
-    resolveNodeRS->dwFlags = pNodeHelper->m_iCreateFlag;
-    if (resolveNodeRS->dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
-      if (pNodeHelper->m_iCurAllStart != -1)
-        resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_CreateNodeMidAll;
+    result.type = m_NodeHelper->m_iCreateFlag;
+    if (result.type == ResolveResult::Type::kCreateNodeOne) {
+      if (m_NodeHelper->m_iCurAllStart != -1)
+        result.type = ResolveResult::Type::kCreateNodeMidAll;
     }
 
-    if (!bNextCreate && (dwStyles & XFA_RESOLVENODE_CreateNode))
-      resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_ExistNodes;
+    if (!bNextCreate && (dwStyles & XFA_ResolveFlag::kCreateNode))
+      result.type = ResolveResult::Type::kExistNodes;
 
-    return !resolveNodeRS->objects.empty();
+    if (result.objects.empty())
+      return absl::nullopt;
+
+    return result;
   }
-  return nNodes > 0;
+  if (nNodes == 0)
+    return absl::nullopt;
+
+  return result;
 }
 
-void CFXJSE_Engine::AddToCacheList(std::unique_ptr<CXFA_List> pList) {
-  m_CacheList.push_back(std::move(pList));
+v8::Local<v8::Object> CFXJSE_Engine::GetOrCreateJSBindingFromMap(
+    CXFA_Object* pObject) {
+  RunVariablesScript(CXFA_Script::FromNode(pObject->AsNode()));
+
+  CJX_Object* pCJXObject = pObject->JSObject();
+  auto iter = m_mapObjectToObject.find(pCJXObject);
+  if (iter != m_mapObjectToObject.end())
+    return v8::Local<v8::Object>::New(GetIsolate(), iter->second);
+
+  v8::Local<v8::Object> binding = pCJXObject->NewBoundV8Object(
+      GetIsolate(), m_pJsClass->GetTemplate(GetIsolate()));
+
+  m_mapObjectToObject[pCJXObject].Reset(GetIsolate(), binding);
+  return binding;
 }
 
-CFXJSE_Value* CFXJSE_Engine::GetOrCreateJSBindingFromMap(CXFA_Object* pObject) {
-  if (pObject->IsNode())
-    RunVariablesScript(pObject->AsNode());
-
-  auto iter = m_mapObjectToValue.find(pObject);
-  if (iter != m_mapObjectToValue.end())
-    return iter->second.get();
-
-  auto jsValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  jsValue->SetHostObject(pObject, m_pJsClass.Get());
-
-  CFXJSE_Value* pValue = jsValue.get();
-  m_mapObjectToValue.insert(std::make_pair(pObject, std::move(jsValue)));
-  return pValue;
-}
-
-void CFXJSE_Engine::RemoveJSBindingFromMap(CXFA_Object* pObject) {
-  auto iter = m_mapObjectToValue.find(pObject);
-  if (iter == m_mapObjectToValue.end())
-    return;
-
-  iter->second->ClearHostObject();
-  m_mapObjectToValue.erase(iter);
-}
-
-void CFXJSE_Engine::SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray) {
+void CFXJSE_Engine::SetNodesOfRunScript(
+    std::vector<cppgc::Persistent<CXFA_Node>>* pArray) {
   m_pScriptNodeArray = pArray;
 }
 
 void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) {
-  if (m_pScriptNodeArray && !pdfium::ContainsValue(*m_pScriptNodeArray, pNode))
-    m_pScriptNodeArray->push_back(pNode);
+  if (m_pScriptNodeArray && !pdfium::Contains(*m_pScriptNodeArray, pNode))
+    m_pScriptNodeArray->emplace_back(pNode);
 }
 
 CXFA_Object* CFXJSE_Engine::ToXFAObject(v8::Local<v8::Value> obj) {
-  if (obj.IsEmpty() || !obj->IsObject())
+  if (!fxv8::IsObject(obj))
     return nullptr;
 
   CFXJSE_HostObject* pHostObj =
       FXJSE_RetrieveObjectBinding(obj.As<v8::Object>());
-  return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
+  if (!pHostObj)
+    return nullptr;
+
+  CJX_Object* pJSObject = pHostObj->AsCJXObject();
+  return pJSObject ? pJSObject->GetXFAObject() : nullptr;
 }
 
-v8::Local<v8::Value> CFXJSE_Engine::NewXFAObject(
-    CXFA_Object* obj,
-    v8::Global<v8::FunctionTemplate>& tmpl) {
+v8::Local<v8::Object> CFXJSE_Engine::NewNormalXFAObject(CXFA_Object* obj) {
   v8::EscapableHandleScope scope(GetIsolate());
-  v8::Local<v8::FunctionTemplate> klass =
-      v8::Local<v8::FunctionTemplate>::New(GetIsolate(), tmpl);
-  v8::Local<v8::Object> object = klass->InstanceTemplate()
-                                     ->NewInstance(m_JsContext->GetContext())
-                                     .ToLocalChecked();
-  FXJSE_UpdateObjectBinding(object, obj);
+  v8::Local<v8::Object> object = obj->JSObject()->NewBoundV8Object(
+      GetIsolate(), GetJseNormalClass()->GetTemplate(GetIsolate()));
   return scope.Escape(object);
 }
diff --git a/fxjs/xfa/cfxjse_engine.h b/fxjs/xfa/cfxjse_engine.h
index addf0cc..c6a95c3 100644
--- a/fxjs/xfa/cfxjse_engine.h
+++ b/fxjs/xfa/cfxjse_engine.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,133 +9,210 @@
 
 #include <map>
 #include <memory>
+#include <type_traits>
 #include <vector>
 
+#include "core/fxcrt/mask.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cfx_v8.h"
-#include "v8/include/v8.h"
+#include "v8/include/cppgc/persistent.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
 
 class CFXJSE_Class;
 class CFXJSE_Context;
 class CFXJSE_FormCalcContext;
+class CFXJSE_HostObject;
+class CFXJSE_NodeHelper;
 class CFXJSE_ResolveProcessor;
+class CFXJSE_Value;
 class CJS_Runtime;
-class CXFA_List;
 
-// Flags for |dwStyles| argument to CFXJSE_Engine::ResolveObjects().
-#define XFA_RESOLVENODE_Children 0x0001
-#define XFA_RESOLVENODE_TagName 0x0002
-#define XFA_RESOLVENODE_Attributes 0x0004
-#define XFA_RESOLVENODE_Properties 0x0008
-#define XFA_RESOLVENODE_Siblings 0x0020
-#define XFA_RESOLVENODE_Parent 0x0040
-#define XFA_RESOLVENODE_AnyChild 0x0080
-#define XFA_RESOLVENODE_ALL 0x0100
-#define XFA_RESOLVENODE_CreateNode 0x0400
-#define XFA_RESOLVENODE_Bind 0x0800
-#define XFA_RESOLVENODE_BindNew 0x1000
+enum class XFA_ResolveFlag : uint16_t {
+  kChildren = 1 << 0,
+  kTagName = 1 << 1,
+  kAttributes = 1 << 2,
+  kProperties = 1 << 3,
+  kSiblings = 1 << 5,
+  kParent = 1 << 6,
+  kAnyChild = 1 << 7,
+  kALL = 1 << 8,
+  kCreateNode = 1 << 10,
+  kBind = 1 << 11,
+  kBindNew = 1 << 12,
+};
 
 class CFXJSE_Engine final : public CFX_V8 {
  public:
+  class ResolveResult {
+    CPPGC_STACK_ALLOCATED();  // Allow raw/unowned pointers.
+
+   public:
+    enum class Type {
+      kNodes = 0,
+      kAttribute,
+      kCreateNodeOne,
+      kCreateNodeAll,
+      kCreateNodeMidAll,
+      kExistNodes,
+    };
+
+    ResolveResult();
+    ResolveResult(const ResolveResult& that);
+    ResolveResult& operator=(const ResolveResult& that);
+    ~ResolveResult();
+
+    Type type = Type::kNodes;
+    XFA_SCRIPTATTRIBUTEINFO script_attribute = {};
+
+    // Vector of Member would be correct for stack-based vectors, if
+    // STL worked with cppgc.
+    std::vector<cppgc::Member<CXFA_Object>> objects;
+  };
+
   static CXFA_Object* ToObject(const v8::FunctionCallbackInfo<v8::Value>& info);
-  static CXFA_Object* ToObject(CFXJSE_Value* pValue);
-  static void GlobalPropertyGetter(CFXJSE_Value* pObject,
+  static CXFA_Object* ToObject(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> value);
+  static CXFA_Object* ToObject(v8::Isolate* pIsolate, CFXJSE_Value* pValue);
+  static CXFA_Object* ToObject(CFXJSE_HostObject* pHostObj);
+  static v8::Local<v8::Value> GlobalPropertyGetter(
+      v8::Isolate* pIsolate,
+      v8::Local<v8::Object> pObject,
+      ByteStringView szPropName);
+  static void GlobalPropertySetter(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Object> pObject,
                                    ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
-  static void GlobalPropertySetter(CFXJSE_Value* pObject,
+                                   v8::Local<v8::Value> pValue);
+  static v8::Local<v8::Value> NormalPropertyGetter(
+      v8::Isolate* pIsolate,
+      v8::Local<v8::Object> pObject,
+      ByteStringView szPropName);
+  static void NormalPropertySetter(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Object> pObject,
                                    ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
-  static void NormalPropertyGetter(CFXJSE_Value* pObject,
-                                   ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
-  static void NormalPropertySetter(CFXJSE_Value* pObject,
-                                   ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
+                                   v8::Local<v8::Value> pValue);
   static CJS_Result NormalMethodCall(
       const v8::FunctionCallbackInfo<v8::Value>& info,
       const WideString& functionName);
-  static int32_t NormalPropTypeGetter(CFXJSE_Value* pObject,
-                                      ByteStringView szPropName,
-                                      bool bQueryIn);
-  static int32_t GlobalPropTypeGetter(CFXJSE_Value* pObject,
-                                      ByteStringView szPropName,
-                                      bool bQueryIn);
+  static FXJSE_ClassPropType NormalPropTypeGetter(v8::Isolate* pIsolate,
+                                                  v8::Local<v8::Object> pObject,
+                                                  ByteStringView szPropName,
+                                                  bool bQueryIn);
+  static FXJSE_ClassPropType GlobalPropTypeGetter(v8::Isolate* pIsolate,
+                                                  v8::Local<v8::Object> pObject,
+                                                  ByteStringView szPropName,
+                                                  bool bQueryIn);
 
   CFXJSE_Engine(CXFA_Document* pDocument, CJS_Runtime* fxjs_runtime);
   ~CFXJSE_Engine() override;
 
-  void SetEventParam(CXFA_EventParam* param) { m_eventParam = param; }
-  CXFA_EventParam* GetEventParam() const { return m_eventParam.Get(); }
+  class EventParamScope {
+    CPPGC_STACK_ALLOCATED();
+
+   public:
+    EventParamScope(CFXJSE_Engine* pEngine,
+                    CXFA_Node* pTarget,
+                    CXFA_EventParam* pEventParam);
+    ~EventParamScope();
+
+   private:
+    UnownedPtr<CFXJSE_Engine> m_pEngine;
+    UnownedPtr<CXFA_Node> m_pPrevTarget;
+    UnownedPtr<CXFA_EventParam> m_pPrevEventParam;
+  };
+  friend class EventParamScope;
+
+  CXFA_Node* GetEventTarget() const { return m_pTarget; }
+  CXFA_EventParam* GetEventParam() const { return m_eventParam; }
   bool RunScript(CXFA_Script::Type eScriptType,
                  WideStringView wsScript,
                  CFXJSE_Value* pRetValue,
                  CXFA_Object* pThisObject);
 
-  bool ResolveObjects(CXFA_Object* refObject,
-                      WideStringView wsExpression,
-                      XFA_RESOLVENODE_RS* resolveNodeRS,
-                      uint32_t dwStyles,
-                      CXFA_Node* bindNode);
+  absl::optional<ResolveResult> ResolveObjects(CXFA_Object* refObject,
+                                               WideStringView wsExpression,
+                                               Mask<XFA_ResolveFlag> dwStyles);
 
-  CFXJSE_Value* GetOrCreateJSBindingFromMap(CXFA_Object* pObject);
-  void RemoveJSBindingFromMap(CXFA_Object* pObject);
+  absl::optional<ResolveResult> ResolveObjectsWithBindNode(
+      CXFA_Object* refObject,
+      WideStringView wsExpression,
+      Mask<XFA_ResolveFlag> dwStyles,
+      CXFA_Node* bindNode);
 
-  void AddToCacheList(std::unique_ptr<CXFA_List> pList);
-  CXFA_Object* GetThisObject() const { return m_pThisObject.Get(); }
+  v8::Local<v8::Object> GetOrCreateJSBindingFromMap(CXFA_Object* pObject);
 
-  void SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray);
+  CXFA_Object* GetThisObject() const { return m_pThisObject; }
+  CFXJSE_Class* GetJseNormalClass() const { return m_pJsClass; }
+  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+
+  void SetNodesOfRunScript(std::vector<cppgc::Persistent<CXFA_Node>>* pArray);
   void AddNodesOfRunScript(CXFA_Node* pNode);
-  CFXJSE_Class* GetJseNormalClass() const { return m_pJsClass.Get(); }
 
   void SetRunAtType(XFA_AttributeValue eRunAt) { m_eRunAtType = eRunAt; }
   bool IsRunAtClient() { return m_eRunAtType != XFA_AttributeValue::Server; }
 
   CXFA_Script::Type GetType();
-  std::vector<CXFA_Node*>* GetUpObjectArray() { return &m_upObjectArray; }
-  CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
+
+  void AddObjectToUpArray(CXFA_Node* pNode);
+  CXFA_Node* LastObjectFromUpArray();
 
   CXFA_Object* ToXFAObject(v8::Local<v8::Value> obj);
-  v8::Local<v8::Value> NewXFAObject(CXFA_Object* obj,
-                                    v8::Global<v8::FunctionTemplate>& tmpl);
+  v8::Local<v8::Object> NewNormalXFAObject(CXFA_Object* obj);
+
+  bool IsResolvingNodes() const { return m_bResolvingNodes; }
+
+  CFXJSE_Context* GetJseContextForTest() const { return GetJseContext(); }
 
  private:
-  CFXJSE_Context* CreateVariablesContext(CXFA_Node* pScriptNode,
+  CFXJSE_Context* GetJseContext() const { return m_JsContext.get(); }
+  CFXJSE_Context* CreateVariablesContext(CXFA_Script* pScriptNode,
                                          CXFA_Node* pSubform);
-  void RemoveBuiltInObjs(CFXJSE_Context* pContext) const;
+  void RemoveBuiltInObjs(CFXJSE_Context* pContext);
   bool QueryNodeByFlag(CXFA_Node* refNode,
                        WideStringView propname,
-                       CFXJSE_Value* pValue,
-                       uint32_t dwFlag,
-                       bool bSetting);
+                       v8::Local<v8::Value>* pValue,
+                       Mask<XFA_ResolveFlag> dwFlag);
+  bool UpdateNodeByFlag(CXFA_Node* refNode,
+                        WideStringView propname,
+                        v8::Local<v8::Value> pValue,
+                        Mask<XFA_ResolveFlag> dwFlag);
   bool IsStrictScopeInJavaScript();
-  CXFA_Object* GetVariablesThis(CXFA_Object* pObject, bool bScriptNode);
-  bool QueryVariableValue(CXFA_Node* pScriptNode,
+  CXFA_Object* GetVariablesThis(CXFA_Object* pObject);
+  CXFA_Object* GetVariablesScript(CXFA_Object* pObject);
+  CFXJSE_Context* VariablesContextForScriptNode(CXFA_Script* pScriptNode);
+  bool QueryVariableValue(CXFA_Script* pScriptNode,
                           ByteStringView szPropName,
-                          CFXJSE_Value* pValue,
-                          bool bGetter);
-  bool RunVariablesScript(CXFA_Node* pScriptNode);
+                          v8::Local<v8::Value>* pValue);
+  bool UpdateVariableValue(CXFA_Script* pScriptNode,
+                           ByteStringView szPropName,
+                           v8::Local<v8::Value> pValue);
+  void RunVariablesScript(CXFA_Script* pScriptNode);
 
   UnownedPtr<CJS_Runtime> const m_pSubordinateRuntime;
-  UnownedPtr<CXFA_Document> const m_pDocument;
+  cppgc::WeakPersistent<CXFA_Document> const m_pDocument;
   std::unique_ptr<CFXJSE_Context> m_JsContext;
   UnownedPtr<CFXJSE_Class> m_pJsClass;
   CXFA_Script::Type m_eScriptType = CXFA_Script::Type::Unknown;
-  std::map<CXFA_Object*, std::unique_ptr<CFXJSE_Value>> m_mapObjectToValue;
-  std::map<CXFA_Object*, std::unique_ptr<CFXJSE_Context>>
+  // |m_mapObjectToValue| is what ensures the v8 object bound to a
+  // CJX_Object remains valid for the lifetime of the engine.
+  std::map<cppgc::Persistent<CJX_Object>, v8::Global<v8::Object>>
+      m_mapObjectToObject;
+  std::map<cppgc::Persistent<CJX_Object>, std::unique_ptr<CFXJSE_Context>>
       m_mapVariableToContext;
+  cppgc::Persistent<CXFA_Node> m_pTarget;
   UnownedPtr<CXFA_EventParam> m_eventParam;
-  std::vector<CXFA_Node*> m_upObjectArray;
-  // CacheList holds the List items so we can clean them up when we're done.
-  std::vector<std::unique_ptr<CXFA_List>> m_CacheList;
-  UnownedPtr<std::vector<CXFA_Node*>> m_pScriptNodeArray;
+  std::vector<cppgc::Persistent<CXFA_Node>> m_upObjectArray;
+  UnownedPtr<std::vector<cppgc::Persistent<CXFA_Node>>> m_pScriptNodeArray;
+  std::unique_ptr<CFXJSE_NodeHelper> const m_NodeHelper;
   std::unique_ptr<CFXJSE_ResolveProcessor> const m_ResolveProcessor;
-  std::unique_ptr<CFXJSE_FormCalcContext> m_FM2JSContext;
-  UnownedPtr<CXFA_Object> m_pThisObject;
+  std::unique_ptr<CFXJSE_FormCalcContext> m_FormCalcContext;
+  cppgc::Persistent<CXFA_Object> m_pThisObject;
   XFA_AttributeValue m_eRunAtType = XFA_AttributeValue::Client;
+  bool m_bResolvingNodes = false;
 };
 
 #endif  //  FXJS_XFA_CFXJSE_ENGINE_H_
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp
index 7a6796c..4fc19d7 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,31 +6,47 @@
 
 #include "fxjs/xfa/cfxjse_formcalc_context.h"
 
-#include <algorithm>
-#include <cstdlib>
-#include <string>
-#include <utility>
+#include <ctype.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
 
-#include "core/fxcrt/cfx_widetextbuf.h"
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/cfx_datetime.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_random.h"
-#include "fxjs/xfa/cfxjse_arguments.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/widetext_buffer.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-function-callback.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
-#include "xfa/fgas/crt/locale_iface.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/fm2js/cxfa_fmparser.h"
-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
+#include "xfa/fxfa/formcalc/cxfa_fmparser.h"
+#include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localevalue.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_thisproxy.h"
 #include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
+#include "xfa/fxfa/parser/gced_locale_iface.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 using pdfium::fxjse::kClassTag;
@@ -38,13 +54,17 @@
 
 namespace {
 
+// Maximum number of characters Acrobat can fit in a text box.
+constexpr int kMaxCharCount = 15654908;
+
 const double kFinancialPrecision = 0.00000001;
 
 const wchar_t kStrCode[] = L"0123456789abcdef";
 
 struct XFA_FMHtmlReserveCode {
-  uint32_t m_uCode;
-  const char* m_htmlReserve;
+  uint16_t m_uCode;
+  // Inline string data reduces size for small strings.
+  const char m_htmlReserve[10];
 };
 
 // Sorted by |m_htmlReserve|.
@@ -181,7 +201,7 @@
     {9824, "spades"}, {9827, "clubs"},  {9829, "hearts"},  {9830, "diams"},
 };
 
-const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFM2JSFunctions[] = {
+const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFunctions[] = {
     {kFuncTag, "Abs", CFXJSE_FormCalcContext::Abs},
     {kFuncTag, "Avg", CFXJSE_FormCalcContext::Avg},
     {kFuncTag, "Ceil", CFXJSE_FormCalcContext::Ceil},
@@ -280,7 +300,7 @@
     255, 2,   255, 255, 255, 255, 255, 255, 255, 255, 255,
     255, 255, 1,   255, 255, 255, 255, 255, 255, 255, 255,
 };
-static_assert(FX_ArraySize(kAltTableDate) == L'a' - L'A' + 1,
+static_assert(std::size(kAltTableDate) == L'a' - L'A' + 1,
               "Invalid kAltTableDate size.");
 
 const uint8_t kAltTableTime[] = {
@@ -288,7 +308,7 @@
     255, 6,   255, 255, 255, 255, 255, 7,   255, 255, 255,
     255, 255, 1,   17,  255, 255, 255, 255, 255, 255, 255,
 };
-static_assert(FX_ArraySize(kAltTableTime) == L'a' - L'A' + 1,
+static_assert(std::size(kAltTableTime) == L'a' - L'A' + 1,
               "Invalid kAltTableTime size.");
 
 void AlternateDateTimeSymbols(WideString* pPattern,
@@ -322,37 +342,34 @@
   }
 }
 
-std::pair<bool, uint32_t> PatternStringType(ByteStringView bsPattern) {
+std::pair<bool, CXFA_LocaleValue::ValueType> PatternStringType(
+    ByteStringView bsPattern) {
   WideString wsPattern = WideString::FromUTF8(bsPattern);
   if (L"datetime" == wsPattern.First(8))
-    return {true, XFA_VT_DATETIME};
+    return {true, CXFA_LocaleValue::ValueType::kDateTime};
   if (L"date" == wsPattern.First(4)) {
     auto pos = wsPattern.Find(L"time");
-    uint32_t type =
-        pos.has_value() && pos.value() != 0 ? XFA_VT_DATETIME : XFA_VT_DATE;
-    return {true, type};
+    if (pos.has_value() && pos.value() != 0)
+      return {true, CXFA_LocaleValue::ValueType::kDateTime};
+    return {true, CXFA_LocaleValue::ValueType::kDate};
   }
   if (L"time" == wsPattern.First(4))
-    return {true, XFA_VT_TIME};
+    return {true, CXFA_LocaleValue::ValueType::kTime};
   if (L"text" == wsPattern.First(4))
-    return {true, XFA_VT_TEXT};
+    return {true, CXFA_LocaleValue::ValueType::kText};
   if (L"num" == wsPattern.First(3)) {
-    uint32_t type;
-    if (L"integer" == wsPattern.Substr(4, 7)) {
-      type = XFA_VT_INTEGER;
-    } else if (L"decimal" == wsPattern.Substr(4, 7)) {
-      type = XFA_VT_DECIMAL;
-    } else if (L"currency" == wsPattern.Substr(4, 8)) {
-      type = XFA_VT_FLOAT;
-    } else if (L"percent" == wsPattern.Substr(4, 7)) {
-      type = XFA_VT_FLOAT;
-    } else {
-      type = XFA_VT_FLOAT;
-    }
-    return {true, type};
+    if (L"integer" == wsPattern.Substr(4, 7))
+      return {true, CXFA_LocaleValue::ValueType::kInteger};
+    if (L"decimal" == wsPattern.Substr(4, 7))
+      return {true, CXFA_LocaleValue::ValueType::kDecimal};
+    if (L"currency" == wsPattern.Substr(4, 8))
+      return {true, CXFA_LocaleValue::ValueType::kFloat};
+    if (L"percent" == wsPattern.Substr(4, 7))
+      return {true, CXFA_LocaleValue::ValueType::kFloat};
+    return {true, CXFA_LocaleValue::ValueType::kFloat};
   }
 
-  uint32_t type = XFA_VT_NULL;
+  CXFA_LocaleValue::ValueType type = CXFA_LocaleValue::ValueType::kNull;
   wsPattern.MakeLower();
   const wchar_t* pData = wsPattern.c_str();
   int32_t iLength = wsPattern.GetLength();
@@ -371,11 +388,11 @@
     }
 
     if (wsPatternChar == 'h' || wsPatternChar == 'k')
-      return {false, XFA_VT_TIME};
+      return {false, CXFA_LocaleValue::ValueType::kTime};
     if (wsPatternChar == 'x' || wsPatternChar == 'o' || wsPatternChar == '0')
-      return {false, XFA_VT_TEXT};
+      return {false, CXFA_LocaleValue::ValueType::kText};
     if (wsPatternChar == 'v' || wsPatternChar == '8' || wsPatternChar == '$')
-      return {false, XFA_VT_FLOAT};
+      return {false, CXFA_LocaleValue::ValueType::kFloat};
     if (wsPatternChar == 'y' || wsPatternChar == 'j') {
       iIndex++;
       wchar_t timePatternChar;
@@ -387,35 +404,31 @@
           continue;
         }
         if (!bSingleQuotation && timePatternChar == 't')
-          return {false, XFA_VT_DATETIME};
+          return {false, CXFA_LocaleValue::ValueType::kDateTime};
         iIndex++;
       }
-      return {false, XFA_VT_DATE};
+      return {false, CXFA_LocaleValue::ValueType::kDate};
     }
 
     if (wsPatternChar == 'a') {
-      type = XFA_VT_TEXT;
+      type = CXFA_LocaleValue::ValueType::kText;
     } else if (wsPatternChar == 'z' || wsPatternChar == 's' ||
                wsPatternChar == 'e' || wsPatternChar == ',' ||
                wsPatternChar == '.') {
-      type = XFA_VT_FLOAT;
+      type = CXFA_LocaleValue::ValueType::kFloat;
     }
     iIndex++;
   }
-
-  if (type == XFA_VT_NULL)
-    type = XFA_VT_TEXT | XFA_VT_FLOAT;
   return {false, type};
 }
 
-CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_Value* pValue) {
-  CFXJSE_HostObject* pHostObj = pValue->ToHostObject();
+CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_HostObject* pHostObj) {
   return pHostObj ? pHostObj->AsFormCalcContext() : nullptr;
 }
 
-LocaleIface* LocaleFromString(CXFA_Document* pDoc,
-                              CXFA_LocaleMgr* pMgr,
-                              ByteStringView bsLocale) {
+GCedLocaleIface* LocaleFromString(CXFA_Document* pDoc,
+                                  CXFA_LocaleMgr* pMgr,
+                                  ByteStringView bsLocale) {
   if (!bsLocale.IsEmpty())
     return pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale));
 
@@ -427,21 +440,21 @@
   if (!bsFormat.IsEmpty())
     return WideString::FromUTF8(bsFormat);
 
-  return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
+  return pLocale->GetDatePattern(LocaleIface::DateTimeSubcategory::kDefault);
 }
 
-FX_LOCALEDATETIMESUBCATEGORY SubCategoryFromInt(int32_t iStyle) {
+LocaleIface::DateTimeSubcategory SubCategoryFromInt(int32_t iStyle) {
   switch (iStyle) {
     case 1:
-      return FX_LOCALEDATETIMESUBCATEGORY_Short;
+      return LocaleIface::DateTimeSubcategory::kShort;
     case 3:
-      return FX_LOCALEDATETIMESUBCATEGORY_Long;
+      return LocaleIface::DateTimeSubcategory::kLong;
     case 4:
-      return FX_LOCALEDATETIMESUBCATEGORY_Full;
+      return LocaleIface::DateTimeSubcategory::kFull;
     case 0:
     case 2:
     default:
-      return FX_LOCALEDATETIMESUBCATEGORY_Medium;
+      return LocaleIface::DateTimeSubcategory::kMedium;
   }
 }
 
@@ -455,7 +468,7 @@
   if (!pLocale)
     return ByteString();
 
-  FX_LOCALEDATETIMESUBCATEGORY category = SubCategoryFromInt(iStyle);
+  LocaleIface::DateTimeSubcategory category = SubCategoryFromInt(iStyle);
   WideString wsLocal = bIsDate ? pLocale->GetDatePattern(category)
                                : pLocale->GetTimePattern(category);
   if (!bStandard)
@@ -469,7 +482,7 @@
 }
 
 bool IsPartOfNumber(char ch) {
-  return std::isdigit(ch) || ch == '-' || ch == '.';
+  return isdigit(ch) || ch == '-' || ch == '.';
 }
 
 bool IsPartOfNumberW(wchar_t ch) {
@@ -517,7 +530,7 @@
   char szYear[5];
   szYear[4] = '\0';
   for (int32_t i = 0; i < 4; ++i) {
-    if (!std::isdigit(pData[i]))
+    if (!isdigit(pData[i]))
       return false;
 
     szYear[i] = pData[i];
@@ -530,7 +543,7 @@
   iStyle = pData[4] == '-' ? 1 : 0;
 
   size_t iPosOff = iStyle == 0 ? 4 : 5;
-  if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1]))
+  if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1]))
     return false;
 
   char szBuffer[3] = {};
@@ -549,7 +562,7 @@
     if (pData.size() == 7)
       return true;
   }
-  if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1]))
+  if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1]))
     return false;
 
   szBuffer[0] = pData[iPosOff];
@@ -595,7 +608,7 @@
   size_t iZone = 0;
   size_t i = 0;
   while (i < pData.size()) {
-    if (!std::isdigit(pData[i]) && pData[i] != ':') {
+    if (!isdigit(pData[i]) && pData[i] != ':') {
       iZone = i;
       break;
     }
@@ -608,11 +621,11 @@
   size_t iPos = 0;
   size_t iIndex = 0;
   while (iIndex < iZone) {
-    if (!std::isdigit(pData[iIndex]))
+    if (!isdigit(pData[iIndex]))
       return false;
 
     szBuffer[0] = pData[iIndex];
-    if (!std::isdigit(pData[iIndex + 1]))
+    if (!isdigit(pData[iIndex + 1]))
       return false;
 
     szBuffer[1] = pData[iIndex + 1];
@@ -654,7 +667,7 @@
     char szMilliSeconds[kSubSecondLength + 1];
     for (int j = 0; j < kSubSecondLength; ++j) {
       char c = pData[iIndex + j];
-      if (!std::isdigit(c))
+      if (!isdigit(c))
         return false;
       szMilliSeconds[j] = c;
     }
@@ -682,11 +695,11 @@
   }
   iPos = 0;
   while (iIndex < pData.size()) {
-    if (!std::isdigit(pData[iIndex]))
+    if (!isdigit(pData[iIndex]))
       return false;
 
     szBuffer[0] = pData[iIndex];
-    if (!std::isdigit(pData[iIndex + 1]))
+    if (!isdigit(pData[iIndex + 1]))
       return false;
 
     szBuffer[1] = pData[iIndex + 1];
@@ -728,45 +741,29 @@
                          int32_t* pMilliSecond,
                          int32_t* pZoneHour,
                          int32_t* pZoneMinute) {
-  int32_t& iYear = *pYear;
-  int32_t& iMonth = *pMonth;
-  int32_t& iDay = *pDay;
-  int32_t& iHour = *pHour;
-  int32_t& iMinute = *pMinute;
-  int32_t& iSecond = *pSecond;
-  int32_t& iMilliSecond = *pMilliSecond;
-  int32_t& iZoneHour = *pZoneHour;
-  int32_t& iZoneMinute = *pZoneMinute;
-
-  iYear = 0;
-  iMonth = 0;
-  iDay = 0;
-  iHour = 0;
-  iMinute = 0;
-  iSecond = 0;
-
-  if (pData.empty())
-    return false;
+  *pYear = 0;
+  *pMonth = 0;
+  *pDay = 0;
+  *pHour = 0;
+  *pMinute = 0;
+  *pSecond = 0;
 
   size_t iIndex = 0;
-  while (pData[iIndex] != 'T' && pData[iIndex] != 't') {
-    if (iIndex >= pData.size())
-      return false;
+  while (iIndex < pData.size()) {
+    if (pData[iIndex] == 'T' || pData[iIndex] == 't')
+      break;
     ++iIndex;
   }
-  if (iIndex != 8 && iIndex != 10)
+  if (iIndex == pData.size() || (iIndex != 8 && iIndex != 10))
     return false;
 
+  pdfium::span<const char> pDateSpan = pData.subspan(0, iIndex);
+  pdfium::span<const char> pTimeSpan = pData.subspan(iIndex + 1);
+
   int32_t iStyle = -1;
-  if (!IsIsoDateFormat(pData.subspan(0, iIndex), &iStyle, &iYear, &iMonth,
-                       &iDay)) {
-    return false;
-  }
-  if (pData[iIndex] != 'T' && pData[iIndex] != 't')
-    return true;
-
-  return IsIsoTimeFormat(pData.subspan(iIndex + 1), &iHour, &iMinute, &iSecond,
-                         &iMilliSecond, &iZoneHour, &iZoneMinute);
+  return IsIsoDateFormat(pDateSpan, &iStyle, pYear, pMonth, pDay) &&
+         IsIsoTimeFormat(pTimeSpan, pHour, pMinute, pSecond, pMilliSecond,
+                         pZoneHour, pZoneMinute);
 }
 
 int32_t DateString2Num(ByteStringView bsDate) {
@@ -820,7 +817,7 @@
     ++dDays;
     ++i;
   }
-  return (int32_t)dDays;
+  return static_cast<int32_t>(dDays);
 }
 
 void GetLocalTimeZone(int32_t* pHour, int32_t* pMin, int32_t* pSec) {
@@ -869,7 +866,7 @@
 WideString DecodeURL(const WideString& wsURL) {
   const wchar_t* pData = wsURL.c_str();
   size_t iLen = wsURL.GetLength();
-  CFX_WideTextBuf wsResultBuf;
+  WideTextBuffer wsResultBuf;
   for (size_t i = 0; i < iLen; ++i) {
     wchar_t ch = pData[i];
     if ('%' != ch) {
@@ -891,14 +888,13 @@
     }
     wsResultBuf.AppendChar(chTemp);
   }
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
 WideString DecodeMLInternal(const WideString& wsHTML, bool bIsHTML) {
   const wchar_t* pData = wsHTML.c_str();
   size_t iLen = wsHTML.GetLength();
-  CFX_WideTextBuf wsResultBuf;
+  WideTextBuffer wsResultBuf;
   for (size_t i = 0; i < iLen; ++i) {
     wchar_t ch = pData[i];
     if (ch != '&') {
@@ -959,8 +955,6 @@
         wsResultBuf.AppendChar('>');
     }
   }
-
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
@@ -980,13 +974,13 @@
                                         '\'', '(', ')', ','};
 
   WideString wsURL = WideString::FromUTF8(bsURL.AsStringView());
-  CFX_WideTextBuf wsResultBuf;
+  WideTextBuffer wsResultBuf;
   wchar_t szEncode[4];
   szEncode[0] = '%';
   szEncode[3] = 0;
   for (wchar_t ch : wsURL) {
     size_t i = 0;
-    size_t iCount = FX_ArraySize(kStrUnsafe);
+    size_t iCount = std::size(kStrUnsafe);
     while (i < iCount) {
       if (ch == kStrUnsafe[i]) {
         int32_t iIndex = ch / 16;
@@ -1001,7 +995,7 @@
       continue;
 
     i = 0;
-    iCount = FX_ArraySize(kStrReserved);
+    iCount = std::size(kStrReserved);
     while (i < iCount) {
       if (ch == kStrReserved[i]) {
         int32_t iIndex = ch / 16;
@@ -1016,7 +1010,7 @@
       continue;
 
     i = 0;
-    iCount = FX_ArraySize(kStrSpecial);
+    iCount = std::size(kStrSpecial);
     while (i < iCount) {
       if (ch == kStrSpecial[i]) {
         wsResultBuf.AppendChar(ch);
@@ -1066,7 +1060,6 @@
       }
     }
   }
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
@@ -1076,7 +1069,7 @@
   szEncode[0] = '&';
   szEncode[1] = '#';
   szEncode[2] = 'x';
-  CFX_WideTextBuf wsResultBuf;
+  WideTextBuffer wsResultBuf;
   for (uint32_t ch : wsHTML) {
     WideString htmlReserve;
     if (HTMLCode2STR(ch, &htmlReserve)) {
@@ -1106,13 +1099,12 @@
       // TODO(tsepez): Handle codepoint not in BMP.
     }
   }
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
 WideString EncodeXML(const ByteString& bsXML) {
   WideString wsXML = WideString::FromUTF8(bsXML.AsStringView());
-  CFX_WideTextBuf wsResultBuf;
+  WideTextBuffer wsResultBuf;
   wchar_t szEncode[9];
   szEncode[0] = '&';
   szEncode[1] = '#';
@@ -1171,25 +1163,21 @@
       }
     }
   }
-  wsResultBuf.AppendChar(0);
   return wsResultBuf.MakeString();
 }
 
 ByteString TrillionUS(ByteStringView bsData) {
-  static const ByteStringView pUnits[] = {"zero",  "one",  "two", "three",
-                                          "four",  "five", "six", "seven",
-                                          "eight", "nine"};
-  static const ByteStringView pCapUnits[] = {"Zero",  "One",  "Two", "Three",
-                                             "Four",  "Five", "Six", "Seven",
-                                             "Eight", "Nine"};
-  static const ByteStringView pTens[] = {
+  static const char kUnits[][6] = {"zero", "one", "two",   "three", "four",
+                                   "five", "six", "seven", "eight", "nine"};
+  static const char kCapUnits[][6] = {"Zero", "One", "Two",   "Three", "Four",
+                                      "Five", "Six", "Seven", "Eight", "Nine"};
+  static const char kTens[][10] = {
       "Ten",     "Eleven",  "Twelve",    "Thirteen", "Fourteen",
       "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
-  static const ByteStringView pLastTens[] = {"Twenty", "Thirty", "Forty",
-                                             "Fifty",  "Sixty",  "Seventy",
-                                             "Eighty", "Ninety"};
-  static const ByteStringView pComm[] = {" Hundred ", " Thousand ", " Million ",
-                                         " Billion ", "Trillion"};
+  static const char kLastTens[][8] = {"Twenty", "Thirty",  "Forty",  "Fifty",
+                                      "Sixty",  "Seventy", "Eighty", "Ninety"};
+  static const char kComm[][11] = {" Hundred ", " Thousand ", " Million ",
+                                   " Billion ", "Trillion"};
   const char* pData = bsData.unterminated_c_str();
   int32_t iLength = bsData.GetLength();
   int32_t iComm = 0;
@@ -1206,89 +1194,86 @@
   if (iFirstCount == 0)
     iFirstCount = 3;
 
-  std::ostringstream strBuf;
+  ByteString strBuf;
   int32_t iIndex = 0;
   if (iFirstCount == 3) {
     if (pData[iIndex] != '0') {
-      strBuf << pCapUnits[pData[iIndex] - '0'];
-      strBuf << pComm[0];
+      strBuf += kCapUnits[pData[iIndex] - '0'];
+      strBuf += kComm[0];
     }
     if (pData[iIndex + 1] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+      strBuf += kCapUnits[pData[iIndex + 2] - '0'];
     } else {
       if (pData[iIndex + 1] > '1') {
-        strBuf << pLastTens[pData[iIndex + 1] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 2] - '0'];
+        strBuf += kLastTens[pData[iIndex + 1] - '2'];
+        strBuf += "-";
+        strBuf += kUnits[pData[iIndex + 2] - '0'];
       } else if (pData[iIndex + 1] == '1') {
-        strBuf << pTens[pData[iIndex + 2] - '0'];
+        strBuf += kTens[pData[iIndex + 2] - '0'];
       } else if (pData[iIndex + 1] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+        strBuf += kCapUnits[pData[iIndex + 2] - '0'];
       }
     }
     iIndex += 3;
   } else if (iFirstCount == 2) {
     if (pData[iIndex] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 1] - '0'];
+      strBuf += kCapUnits[pData[iIndex + 1] - '0'];
     } else {
       if (pData[iIndex] > '1') {
-        strBuf << pLastTens[pData[iIndex] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 1] - '0'];
+        strBuf += kLastTens[pData[iIndex] - '2'];
+        strBuf += "-";
+        strBuf += kUnits[pData[iIndex + 1] - '0'];
       } else if (pData[iIndex] == '1') {
-        strBuf << pTens[pData[iIndex + 1] - '0'];
+        strBuf += kTens[pData[iIndex + 1] - '0'];
       } else if (pData[iIndex] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 1] - '0'];
+        strBuf += kCapUnits[pData[iIndex + 1] - '0'];
       }
     }
     iIndex += 2;
   } else if (iFirstCount == 1) {
-    strBuf << pCapUnits[pData[iIndex] - '0'];
+    strBuf += kCapUnits[pData[iIndex] - '0'];
     ++iIndex;
   }
   if (iLength > 3 && iFirstCount > 0) {
-    strBuf << pComm[iComm];
+    strBuf += kComm[iComm];
     --iComm;
   }
   while (iIndex < iLength) {
     if (pData[iIndex] != '0') {
-      strBuf << pCapUnits[pData[iIndex] - '0'];
-      strBuf << pComm[0];
+      strBuf += kCapUnits[pData[iIndex] - '0'];
+      strBuf += kComm[0];
     }
     if (pData[iIndex + 1] == '0') {
-      strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+      strBuf += kCapUnits[pData[iIndex + 2] - '0'];
     } else {
       if (pData[iIndex + 1] > '1') {
-        strBuf << pLastTens[pData[iIndex + 1] - '2'];
-        strBuf << "-";
-        strBuf << pUnits[pData[iIndex + 2] - '0'];
+        strBuf += kLastTens[pData[iIndex + 1] - '2'];
+        strBuf += "-";
+        strBuf += kUnits[pData[iIndex + 2] - '0'];
       } else if (pData[iIndex + 1] == '1') {
-        strBuf << pTens[pData[iIndex + 2] - '0'];
+        strBuf += kTens[pData[iIndex + 2] - '0'];
       } else if (pData[iIndex + 1] == '0') {
-        strBuf << pCapUnits[pData[iIndex + 2] - '0'];
+        strBuf += kCapUnits[pData[iIndex + 2] - '0'];
       }
     }
     if (iIndex < iLength - 3) {
-      strBuf << pComm[iComm];
+      strBuf += kComm[iComm];
       --iComm;
     }
     iIndex += 3;
   }
-  return ByteString(strBuf);
+  return strBuf;
 }
 
-ByteString WordUS(const ByteString& bsData, int32_t iStyle) {
-  const char* pData = bsData.c_str();
-  int32_t iLength = bsData.GetLength();
-  if (iStyle < 0 || iStyle > 2) {
+ByteString WordUS(ByteStringView bsData, int32_t iStyle) {
+  if (iStyle < 0 || iStyle > 2)
     return ByteString();
-  }
 
-  std::ostringstream strBuf;
-
+  int32_t iLength = bsData.GetLength();
+  ByteString strBuf;
   int32_t iIndex = 0;
   while (iIndex < iLength) {
-    if (pData[iIndex] == '.')
+    if (bsData[iIndex] == '.')
       break;
     ++iIndex;
   }
@@ -1299,535 +1284,641 @@
     if (!iCount && iInteger - iIndex > 0)
       iCount = 12;
 
-    strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
+    strBuf += TrillionUS(bsData.Substr(iIndex, iCount));
     iIndex += iCount;
     if (iIndex < iInteger)
-      strBuf << " Trillion ";
+      strBuf += " Trillion ";
   }
 
   if (iStyle > 0)
-    strBuf << " Dollars";
+    strBuf += " Dollars";
 
   if (iStyle > 1 && iInteger < iLength) {
-    strBuf << " And ";
+    strBuf += " And ";
     iIndex = iInteger + 1;
     while (iIndex < iLength) {
       int32_t iCount = (iLength - iIndex) % 12;
       if (!iCount && iLength - iIndex > 0)
         iCount = 12;
 
-      strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
+      strBuf += TrillionUS(bsData.Substr(iIndex, iCount));
       iIndex += iCount;
       if (iIndex < iLength)
-        strBuf << " Trillion ";
+        strBuf += " Trillion ";
     }
-    strBuf << " Cents";
+    strBuf += " Cents";
   }
-  return ByteString(strBuf);
+  return strBuf;
+}
+
+v8::Local<v8::Value> GetObjectDefaultValue(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Object> pObject) {
+  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject));
+  if (!pNode)
+    return fxv8::NewNullHelper(pIsolate);
+
+  v8::Local<v8::Value> value;
+  pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &value, false,
+                                           XFA_Attribute::Unknown);
+  return value;
+}
+
+bool SetObjectDefaultValue(v8::Isolate* pIsolate,
+                           v8::Local<v8::Object> pObject,
+                           v8::Local<v8::Value> hNewValue) {
+  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject));
+  if (!pNode)
+    return false;
+
+  pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &hNewValue, true,
+                                           XFA_Attribute::Unknown);
+  return true;
+}
+
+v8::Local<v8::Value> GetExtractedValue(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value> pValue) {
+  if (pValue.IsEmpty())
+    return v8::Local<v8::Value>();
+
+  if (fxv8::IsArray(pValue)) {
+    v8::Local<v8::Array> arr = pValue.As<v8::Array>();
+    uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
+    if (iLength < 3)
+      return fxv8::NewUndefinedHelper(pIsolate);
+
+    v8::Local<v8::Value> propertyValue =
+        fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
+    v8::Local<v8::Value> jsValue =
+        fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2);
+    if (!fxv8::IsObject(jsValue))
+      return fxv8::NewUndefinedHelper(pIsolate);
+
+    v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+    if (fxv8::IsNull(propertyValue))
+      return GetObjectDefaultValue(pIsolate, jsObjectValue);
+
+    ByteString bsName =
+        fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
+    return fxv8::ReentrantGetObjectPropertyHelper(pIsolate, jsObjectValue,
+                                                  bsName.AsStringView());
+  }
+
+  if (fxv8::IsObject(pValue))
+    return GetObjectDefaultValue(pIsolate, pValue.As<v8::Object>());
+
+  return pValue;
+}
+
+v8::Local<v8::Value> GetSimpleValue(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    uint32_t index) {
+  DCHECK(index < static_cast<uint32_t>(info.Length()));
+  return GetExtractedValue(info.GetIsolate(), info[index]);
+}
+
+bool ValueIsNull(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
+  return extracted.IsEmpty() || fxv8::IsNull(extracted);
+}
+
+int32_t ValueToInteger(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
+  if (extracted.IsEmpty())
+    return 0;
+
+  if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
+    return ValueToInteger(pIsolate, extracted);
+
+  if (fxv8::IsString(extracted)) {
+    ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
+    return FXSYS_atoi(bsValue.c_str());
+  }
+
+  return fxv8::ReentrantToInt32Helper(pIsolate, extracted);
+}
+
+float ValueToFloat(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
+  if (extracted.IsEmpty())
+    return 0.0f;
+
+  if (fxv8::IsUndefined(extracted))
+    return 0.0f;
+
+  if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
+    return ValueToFloat(pIsolate, extracted);
+
+  if (fxv8::IsString(extracted)) {
+    ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
+    return strtof(bsValue.c_str(), nullptr);
+  }
+
+  return fxv8::ReentrantToFloatHelper(pIsolate, extracted);
+}
+
+double ValueToDouble(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  v8::Local<v8::Value> extracted = GetExtractedValue(pIsolate, arg);
+  if (extracted.IsEmpty())
+    return 0.0;
+
+  if (fxv8::IsUndefined(extracted))
+    return 0.0;
+
+  if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted))
+    return ValueToDouble(pIsolate, extracted);
+
+  if (fxv8::IsString(extracted)) {
+    ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted);
+    return strtod(bsValue.c_str(), nullptr);
+  }
+
+  return fxv8::ReentrantToDoubleHelper(pIsolate, extracted);
+}
+
+absl::optional<double> ExtractDouble(v8::Isolate* pIsolate,
+                                     v8::Local<v8::Value> src) {
+  if (src.IsEmpty())
+    return 0.0;
+
+  if (!fxv8::IsArray(src))
+    return ValueToDouble(pIsolate, src);
+
+  v8::Local<v8::Array> arr = src.As<v8::Array>();
+  uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
+  if (iLength < 3)
+    return absl::nullopt;
+
+  v8::Local<v8::Value> propertyValue =
+      fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
+  v8::Local<v8::Value> jsValue =
+      fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2);
+  if (fxv8::IsNull(propertyValue) || !fxv8::IsObject(jsValue))
+    return ValueToDouble(pIsolate, jsValue);
+
+  ByteString bsName =
+      fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
+  return ValueToDouble(
+      pIsolate, fxv8::ReentrantGetObjectPropertyHelper(
+                    pIsolate, jsValue.As<v8::Object>(), bsName.AsStringView()));
+}
+
+ByteString ValueToUTF8String(v8::Isolate* pIsolate, v8::Local<v8::Value> arg) {
+  if (arg.IsEmpty())
+    return ByteString();
+
+  if (fxv8::IsNull(arg) || fxv8::IsUndefined(arg))
+    return ByteString();
+
+  if (fxv8::IsBoolean(arg))
+    return fxv8::ReentrantToBooleanHelper(pIsolate, arg) ? "1" : "0";
+
+  return fxv8::ReentrantToByteStringHelper(pIsolate, arg);
+}
+
+bool SimpleValueCompare(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value> firstValue,
+                        v8::Local<v8::Value> secondValue) {
+  if (firstValue.IsEmpty())
+    return false;
+
+  if (fxv8::IsString(firstValue)) {
+    const ByteString first = ValueToUTF8String(pIsolate, firstValue);
+    const ByteString second = ValueToUTF8String(pIsolate, secondValue);
+    return first == second;
+  }
+  if (fxv8::IsNumber(firstValue)) {
+    const float first = ValueToFloat(pIsolate, firstValue);
+    const float second = ValueToFloat(pIsolate, secondValue);
+    return first == second;
+  }
+  if (fxv8::IsBoolean(firstValue)) {
+    const bool first = fxv8::ReentrantToBooleanHelper(pIsolate, firstValue);
+    const bool second = fxv8::ReentrantToBooleanHelper(pIsolate, secondValue);
+    return first == second;
+  }
+  return fxv8::IsNull(firstValue) && fxv8::IsNull(secondValue);
+}
+
+std::vector<v8::Local<v8::Value>> UnfoldArgs(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  std::vector<v8::Local<v8::Value>> results;
+  v8::Isolate* pIsolate = info.GetIsolate();
+  for (int i = 1; i < info.Length(); ++i) {
+    v8::Local<v8::Value> arg = info[i];
+    if (fxv8::IsArray(arg)) {
+      v8::Local<v8::Array> arr = arg.As<v8::Array>();
+      uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
+      if (iLength < 3)
+        continue;
+
+      v8::Local<v8::Value> propertyValue =
+          fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
+
+      for (uint32_t j = 2; j < iLength; j++) {
+        v8::Local<v8::Value> jsValue =
+            fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, j);
+
+        if (!fxv8::IsObject(jsValue)) {
+          results.push_back(fxv8::NewUndefinedHelper(pIsolate));
+        } else if (fxv8::IsNull(propertyValue)) {
+          results.push_back(
+              GetObjectDefaultValue(pIsolate, jsValue.As<v8::Object>()));
+        } else {
+          ByteString bsName =
+              fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
+          results.push_back(fxv8::ReentrantGetObjectPropertyHelper(
+              pIsolate, jsValue.As<v8::Object>(), bsName.AsStringView()));
+        }
+      }
+    } else if (fxv8::IsObject(arg)) {
+      results.push_back(GetObjectDefaultValue(pIsolate, arg.As<v8::Object>()));
+    } else {
+      results.push_back(arg);
+    }
+  }
+  return results;
+}
+
+// Returns empty value on failure.
+v8::Local<v8::Value> GetObjectForName(CFXJSE_HostObject* pHostObject,
+                                      ByteStringView bsAccessorName) {
+  CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument();
+  if (!pDoc)
+    return v8::Local<v8::Value>();
+
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      pScriptContext->ResolveObjects(
+          pScriptContext->GetThisObject(),
+          WideString::FromUTF8(bsAccessorName).AsStringView(),
+          Mask<XFA_ResolveFlag>{
+              XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
+              XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent});
+  if (!maybeResult.has_value() ||
+      maybeResult.value().type != CFXJSE_Engine::ResolveResult::Type::kNodes ||
+      maybeResult.value().objects.empty()) {
+    return v8::Local<v8::Value>();
+  }
+  return pScriptContext->GetOrCreateJSBindingFromMap(
+      maybeResult.value().objects.front().Get());
+}
+
+absl::optional<CFXJSE_Engine::ResolveResult> ResolveObjects(
+    CFXJSE_HostObject* pHostObject,
+    v8::Local<v8::Value> pRefValue,
+    ByteStringView bsSomExp,
+    bool bDotAccessor,
+    bool bHasNoResolveName) {
+  CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument();
+  if (!pDoc)
+    return absl::nullopt;
+
+  v8::Isolate* pIsolate = ToFormCalcContext(pHostObject)->GetIsolate();
+  WideString wsSomExpression = WideString::FromUTF8(bsSomExp);
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  CXFA_Object* pNode = nullptr;
+  Mask<XFA_ResolveFlag> dwFlags;
+  if (bDotAccessor) {
+    if (fxv8::IsNull(pRefValue)) {
+      pNode = pScriptContext->GetThisObject();
+      dwFlags = {XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent};
+    } else {
+      pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue);
+      if (!pNode)
+        return absl::nullopt;
+
+      if (bHasNoResolveName) {
+        WideString wsName;
+        if (CXFA_Node* pXFANode = pNode->AsNode()) {
+          absl::optional<WideString> ret =
+              pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false);
+          if (ret.has_value())
+            wsName = ret.value();
+        }
+        if (wsName.IsEmpty())
+          wsName = L"#" + WideString::FromASCII(pNode->GetClassName());
+
+        wsSomExpression = wsName + wsSomExpression;
+        dwFlags = XFA_ResolveFlag::kSiblings;
+      } else {
+        dwFlags = (bsSomExp == "*")
+                      ? Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren}
+                      : Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
+                                              XFA_ResolveFlag::kAttributes,
+                                              XFA_ResolveFlag::kProperties};
+      }
+    }
+  } else {
+    pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue);
+    dwFlags = XFA_ResolveFlag::kAnyChild;
+  }
+  return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(),
+                                        dwFlags);
+}
+
+std::vector<v8::Local<v8::Value>> ParseResolveResult(
+    CFXJSE_HostObject* pHostObject,
+    const CFXJSE_Engine::ResolveResult& resolveNodeRS,
+    v8::Local<v8::Value> pParentValue,
+    bool* bAttribute) {
+  std::vector<v8::Local<v8::Value>> resultValues;
+  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pHostObject);
+  v8::Isolate* pIsolate = pContext->GetIsolate();
+
+  if (resolveNodeRS.type == CFXJSE_Engine::ResolveResult::Type::kNodes) {
+    *bAttribute = false;
+    CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext();
+    for (auto& pObject : resolveNodeRS.objects) {
+      resultValues.push_back(
+          pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get()));
+    }
+    return resultValues;
+  }
+
+  *bAttribute = true;
+  if (resolveNodeRS.script_attribute.callback &&
+      resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
+    for (auto& pObject : resolveNodeRS.objects) {
+      v8::Local<v8::Value> pValue;
+      CJX_Object* jsObject = pObject->JSObject();
+      (*resolveNodeRS.script_attribute.callback)(
+          pIsolate, jsObject, &pValue, false,
+          resolveNodeRS.script_attribute.attribute);
+      resultValues.push_back(pValue);
+      *bAttribute = false;
+    }
+  }
+  if (*bAttribute && fxv8::IsObject(pParentValue))
+    resultValues.push_back(pParentValue);
+
+  return resultValues;
+}
+
+// Returns 0 if the provided `arg` is an invalid payment period count.
+int GetValidatedPaymentPeriods(v8::Isolate* isolate, v8::Local<v8::Value> arg) {
+  double periods = ValueToDouble(isolate, arg);
+  if (periods < 1 ||
+      periods > static_cast<double>(std::numeric_limits<int32_t>::max())) {
+    return 0;
+  }
+
+  return static_cast<int>(periods);
 }
 
 }  // namespace
 
-const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor = {
-    kClassTag,                              // tag
-    "XFA_FM2JS_FormCalcClass",              // name
-    kFormCalcFM2JSFunctions,                // methods
-    FX_ArraySize(kFormCalcFM2JSFunctions),  // number of methods
-    nullptr,                                // dynamic prop type
-    nullptr,                                // dynamic prop getter
-    nullptr,                                // dynamic prop setter
-    nullptr,                                // dynamic prop method call
+const FXJSE_CLASS_DESCRIPTOR kFormCalcDescriptor = {
+    kClassTag,                      // tag
+    "XFA_FormCalcClass",            // name
+    kFormCalcFunctions,             // methods
+    std::size(kFormCalcFunctions),  // number of methods
+    nullptr,                        // dynamic prop type
+    nullptr,                        // dynamic prop getter
+    nullptr,                        // dynamic prop setter
+    nullptr,                        // dynamic prop method call
 };
 
 // static
-void CFXJSE_FormCalcContext::Abs(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Abs");
+void CFXJSE_FormCalcContext::Abs(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Abs");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  if (ValueIsNull(info.GetIsolate(), info[0])) {
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  double dValue = ValueToDouble(pThis, argOne.get());
+  double dValue = ValueToDouble(info.GetIsolate(), info[0]);
   if (dValue < 0)
     dValue = -dValue;
 
-  args.GetReturnValue()->SetDouble(dValue);
+  info.GetReturnValue().Set(dValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::Avg(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc < 1) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
+void CFXJSE_FormCalcContext::Avg(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   uint32_t uCount = 0;
   double dSum = 0.0;
-  for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+  auto fn = [&uCount, &dSum](v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue) {
+    dSum += ValueToDouble(pIsolate, pValue);
+    uCount++;
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/false))
+    return;
 
-    if (!argValue->IsArray()) {
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-      continue;
-    }
-
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argValue->GetObjectProperty("length", lengthValue.get());
-    int32_t iLength = lengthValue->ToInteger();
-
-    if (iLength > 2) {
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          auto defaultPropValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-          GetObjectDefaultValue(jsObjectValue.get(), defaultPropValue.get());
-          if (defaultPropValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, defaultPropValue.get());
-          uCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, newPropertyValue.get());
-          uCount++;
-        }
-      }
-    }
-  }
   if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetDouble(dSum / uCount);
+  info.GetReturnValue().Set(dSum / uCount);
 }
 
 // static
-void CFXJSE_FormCalcContext::Ceil(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Ceil");
+void CFXJSE_FormCalcContext::Ceil(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ceil");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(ceil(ValueToFloat(pThis, argValue.get())));
+  info.GetReturnValue().Set(ceil(ValueToFloat(info.GetIsolate(), argValue)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Count(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  int32_t iCount = 0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+void CFXJSE_FormCalcContext::Count(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  uint32_t iCount = 0;
+  auto fn = [&iCount](v8::Isolate* pIsolate, v8::Local<v8::Value> pvalue) {
+    ++iCount;
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
+    return;
 
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (!newPropertyValue->IsNull())
-            iCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          iCount += newPropertyValue->IsNull() ? 0 : 1;
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (!newPropertyValue->IsNull())
-        iCount++;
-    } else {
-      iCount++;
-    }
-  }
-  args.GetReturnValue()->SetInteger(iCount);
+  info.GetReturnValue().Set(iCount);
 }
 
 // static
-void CFXJSE_FormCalcContext::Floor(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Floor");
+void CFXJSE_FormCalcContext::Floor(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Floor");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(floor(ValueToFloat(pThis, argValue.get())));
+  info.GetReturnValue().Set(floor(ValueToFloat(info.GetIsolate(), argValue)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Max(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+void CFXJSE_FormCalcContext::Max(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   uint32_t uCount = 0;
   double dMaxValue = 0.0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+  auto fn = [&uCount, &dMaxValue](v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value> pValue) {
+    ++uCount;
+    double dValue = ValueToDouble(pIsolate, pValue);
+    dMaxValue = uCount == 1 ? dValue : std::max(dMaxValue, dValue);
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
+    return;
 
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      uCount++;
-      double dValue = ValueToDouble(pThis, newPropertyValue.get());
-      dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-    } else {
-      uCount++;
-      double dValue = ValueToDouble(pThis, argValue.get());
-      dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
-    }
-  }
   if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetDouble(dMaxValue);
+  info.GetReturnValue().Set(dMaxValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::Min(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+void CFXJSE_FormCalcContext::Min(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   uint32_t uCount = 0;
   double dMinValue = 0.0;
-  for (int32_t i = 0; i < args.GetLength(); i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+  auto fn = [&uCount, &dMinValue](v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value> pValue) {
+    ++uCount;
+    double dValue = ValueToDouble(pIsolate, pValue);
+    dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
+    return;
 
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          uCount++;
-          double dValue = ValueToDouble(pThis, newPropertyValue.get());
-          dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      uCount++;
-      double dValue = ValueToDouble(pThis, newPropertyValue.get());
-      dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-    } else {
-      uCount++;
-      double dValue = ValueToDouble(pThis, argValue.get());
-      dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
-    }
-  }
   if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetDouble(dMinValue);
+  info.GetReturnValue().Set(dMinValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::Mod(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Mod(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 2) {
-    pContext->ThrowParamCountMismatchException(L"Mod");
+  if (info.Length() != 2) {
+    pContext->ThrowParamCountMismatchException("Mod");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
-  if (argOne->IsNull() || argTwo->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  if (fxv8::IsNull(info[0]) || fxv8::IsNull(info[1])) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  bool argOneResult;
-  double dDividend = ExtractDouble(pThis, argOne.get(), &argOneResult);
-  bool argTwoResult;
-  double dDivisor = ExtractDouble(pThis, argTwo.get(), &argTwoResult);
-  if (!argOneResult || !argTwoResult) {
+  absl::optional<double> maybe_dividend =
+      ExtractDouble(info.GetIsolate(), info[0]);
+  absl::optional<double> maybe_divisor =
+      ExtractDouble(info.GetIsolate(), info[1]);
+  if (!maybe_dividend.has_value() || !maybe_divisor.has_value()) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  if (dDivisor == 0.0) {
+  double dividend = maybe_dividend.value();
+  double divisor = maybe_divisor.value();
+  if (divisor == 0.0) {
     pContext->ThrowDivideByZeroException();
     return;
   }
 
-  args.GetReturnValue()->SetDouble(dDividend -
-                                   dDivisor * (int32_t)(dDividend / dDivisor));
+  info.GetReturnValue().Set(dividend -
+                            divisor * static_cast<int32_t>(dividend / divisor));
 }
 
 // static
-void CFXJSE_FormCalcContext::Round(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Round(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    pContext->ThrowParamCountMismatchException(L"Round");
+    pContext->ThrowParamCountMismatchException("Round");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  if (fxv8::IsNull(info[0])) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  bool dValueRet;
-  double dValue = ExtractDouble(pThis, argOne.get(), &dValueRet);
-  if (!dValueRet) {
+  absl::optional<double> maybe_value =
+      ExtractDouble(info.GetIsolate(), info[0]);
+  if (!maybe_value.has_value()) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
+  double dValue = maybe_value.value();
   uint8_t uPrecision = 0;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
-    if (argTwo->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    if (fxv8::IsNull(info[1])) {
+      info.GetReturnValue().SetNull();
       return;
     }
-
-    bool dPrecisionRet;
-    double dPrecision = ExtractDouble(pThis, argTwo.get(), &dPrecisionRet);
-    if (!dPrecisionRet) {
+    absl::optional<double> maybe_precision =
+        ExtractDouble(info.GetIsolate(), info[1]);
+    if (!maybe_precision.has_value()) {
       pContext->ThrowArgumentMismatchException();
       return;
     }
-
+    double dPrecision = maybe_precision.value();
     uPrecision = static_cast<uint8_t>(pdfium::clamp(dPrecision, 0.0, 12.0));
   }
 
   CFGAS_Decimal decimalValue(static_cast<float>(dValue), uPrecision);
-  args.GetReturnValue()->SetDouble(decimalValue.ToDouble());
+  info.GetReturnValue().Set(decimalValue.ToDouble());
 }
 
 // static
-void CFXJSE_FormCalcContext::Sum(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
-  if (argc == 0) {
-    args.GetReturnValue()->SetNull();
-    return;
-  }
-
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
+void CFXJSE_FormCalcContext::Sum(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   uint32_t uCount = 0;
   double dSum = 0.0;
-  for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
-    if (argValue->IsNull())
-      continue;
+  auto fn = [&uCount, &dSum](v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue) {
+    ++uCount;
+    dSum += ValueToDouble(pIsolate, pValue);
+  };
+  if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true))
+    return;
 
-    if (argValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength <= 2) {
-        pContext->ThrowArgumentMismatchException();
-        return;
-      }
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValue->GetObjectPropertyByIdx(1, propertyValue.get());
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, jsObjectValue.get());
-          uCount++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
-          if (newPropertyValue->IsNull())
-            continue;
-
-          dSum += ValueToDouble(pThis, newPropertyValue.get());
-          uCount++;
-        }
-      }
-    } else if (argValue->IsObject()) {
-      auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
-      if (newPropertyValue->IsNull())
-        continue;
-
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-    } else {
-      dSum += ValueToDouble(pThis, argValue.get());
-      uCount++;
-    }
-  }
   if (uCount == 0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetDouble(dSum);
+  info.GetReturnValue().Set(dSum);
 }
 
 // static
-void CFXJSE_FormCalcContext::Date(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 0) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date");
+void CFXJSE_FormCalcContext::Date(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 0) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date");
     return;
   }
 
@@ -1835,140 +1926,141 @@
   FXSYS_time(&currentTime);
   struct tm* pTmStruct = gmtime(&currentTime);
 
-  args.GetReturnValue()->SetInteger(DateString2Num(
+  info.GetReturnValue().Set(DateString2Num(
       ByteString::Format("%d%02d%02d", pTmStruct->tm_year + 1900,
                          pTmStruct->tm_mon + 1, pTmStruct->tm_mday)
           .AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Date2Num(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Date2Num(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date2Num");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, dateValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> dateValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), dateValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsDate = ValueToUTF8String(dateValue.get());
+  ByteString bsDate = ValueToUTF8String(info.GetIsolate(), dateValue);
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (ValueIsNull(info.GetIsolate(), formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localeValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (ValueIsNull(info.GetIsolate(), localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   ByteString bsIsoDate =
       Local2IsoDate(pThis, bsDate.AsStringView(), bsFormat.AsStringView(),
                     bsLocale.AsStringView());
-  args.GetReturnValue()->SetInteger(DateString2Num(bsIsoDate.AsStringView()));
+  info.GetReturnValue().Set(DateString2Num(bsIsoDate.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::DateFmt(CFXJSE_Value* pThis,
-                                     ByteStringView bsFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::DateFmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date2Num");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num");
     return;
   }
 
   int32_t iStyle = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
+    if (fxv8::IsNull(infotyle)) {
+      info.GetReturnValue().SetNull();
       return;
     }
 
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
     if (iStyle < 0 || iStyle > 4)
       iStyle = 0;
   }
 
   ByteString bsLocale;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
-    if (argLocale->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(argLocale)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(argLocale.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
   }
 
   ByteString bsFormat =
       GetStandardDateFormat(pThis, iStyle, bsLocale.AsStringView());
-  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::IsoDate2Num(CFXJSE_Value* pThis,
-                                         ByteStringView bsFuncName,
-                                         CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"IsoDate2Num");
+void CFXJSE_FormCalcContext::IsoDate2Num(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("IsoDate2Num");
     return;
   }
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  ByteString bsArg = ValueToUTF8String(argOne.get());
-  args.GetReturnValue()->SetInteger(DateString2Num(bsArg.AsStringView()));
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
+  info.GetReturnValue().Set(DateString2Num(bsArg.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::IsoTime2Num(CFXJSE_Value* pThis,
-                                         ByteStringView bsFuncName,
-                                         CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::IsoTime2Num(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"IsoTime2Num");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("IsoTime2Num");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
   CXFA_Document* pDoc = pContext->GetDocument();
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  ByteString bsArg = ValueToUTF8String(argOne.get());
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
   auto pos = bsArg.Find('T', 0);
   if (!pos.has_value() || pos.value() == bsArg.GetLength() - 1) {
-    args.GetReturnValue()->SetInteger(0);
+    info.GetReturnValue().Set(0);
     return;
   }
   bsArg = bsArg.Last(bsArg.GetLength() - (pos.value() + 1));
 
-  CXFA_LocaleValue timeValue(XFA_VT_TIME,
+  CXFA_LocaleValue timeValue(CXFA_LocaleValue::ValueType::kTime,
                              WideString::FromUTF8(bsArg.AsStringView()), pMgr);
   if (!timeValue.IsValid()) {
-    args.GetReturnValue()->SetInteger(0);
+    info.GetReturnValue().Set(0);
     return;
   }
 
@@ -1981,7 +2073,7 @@
   // TODO(dsinclair): See if there is other time conversion code in pdfium and
   //   consolidate.
   int32_t mins = hour * 60 + min;
-  mins -= (pMgr->GetDefLocale()->GetTimeZone().tzHour * 60);
+  mins -= pMgr->GetDefLocale()->GetTimeZoneInMinutes();
   while (mins > 1440)
     mins -= 1440;
   while (mins < 0)
@@ -1989,123 +2081,126 @@
   hour = mins / 60;
   min = mins % 60;
 
-  args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
-                                    second * 1000 + milSecond + 1);
+  info.GetReturnValue().Set(hour * 3600000 + min * 60000 + second * 1000 +
+                            milSecond + 1);
 }
 
 // static
-void CFXJSE_FormCalcContext::LocalDateFmt(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::LocalDateFmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"LocalDateFmt");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalDateFmt");
     return;
   }
 
   int32_t iStyle = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
+    if (fxv8::IsNull(infotyle)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
     if (iStyle > 4 || iStyle < 0)
       iStyle = 0;
   }
 
   ByteString bsLocale;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
-    if (argLocale->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(argLocale)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(argLocale.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
   }
 
   ByteString bsFormat =
       GetLocalDateFormat(pThis, iStyle, bsLocale.AsStringView(), false);
-  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::LocalTimeFmt(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::LocalTimeFmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"LocalTimeFmt");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalTimeFmt");
     return;
   }
 
   int32_t iStyle = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
+    if (fxv8::IsNull(infotyle)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
     if (iStyle > 4 || iStyle < 0)
       iStyle = 0;
   }
 
   ByteString bsLocale;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
-    if (argLocale->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(argLocale)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(argLocale.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
   }
 
   ByteString bsFormat =
       GetLocalTimeFormat(pThis, iStyle, bsLocale.AsStringView(), false);
-  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Num2Date(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Num2Date(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2Date");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Date");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, dateValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> dateValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), dateValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  int32_t dDate = (int32_t)ValueToFloat(pThis, dateValue.get());
+  int32_t dDate =
+      static_cast<int32_t>(ValueToFloat(info.GetIsolate(), dateValue));
   if (dDate < 1) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
 
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (ValueIsNull(info.GetIsolate(), formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localeValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (ValueIsNull(info.GetIsolate(), localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   int32_t iYear = 1900;
@@ -2204,242 +2299,247 @@
       pThis,
       ByteString::Format("%d%02d%02d", iYear + i, iMonth, iDay).AsStringView(),
       bsFormat.AsStringView(), bsLocale.AsStringView());
-  args.GetReturnValue()->SetString(bsLocalDate.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsLocalDate.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Num2GMTime(CFXJSE_Value* pThis,
-                                        ByteStringView bsFuncName,
-                                        CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Num2GMTime(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2GMTime");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2GMTime");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (timeValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(timeValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  int32_t iTime = (int32_t)ValueToFloat(pThis, timeValue.get());
+  int32_t iTime =
+      static_cast<int32_t>(ValueToFloat(info.GetIsolate(), timeValue));
   if (abs(iTime) < 1.0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
 
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (formatValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (localeValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (fxv8::IsNull(localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   ByteString bsGMTTime = Num2AllTime(pThis, iTime, bsFormat.AsStringView(),
                                      bsLocale.AsStringView(), true);
-  args.GetReturnValue()->SetString(bsGMTTime.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsGMTTime.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Num2Time(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Num2Time(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2Time");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Time");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (timeValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(timeValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  float fTime = ValueToFloat(pThis, timeValue.get());
+  float fTime = ValueToFloat(info.GetIsolate(), timeValue);
   if (fabs(fTime) < 1.0) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
 
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (formatValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (localeValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (fxv8::IsNull(localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   ByteString bsLocalTime =
       Num2AllTime(pThis, static_cast<int32_t>(fTime), bsFormat.AsStringView(),
                   bsLocale.AsStringView(), false);
-  args.GetReturnValue()->SetString(bsLocalTime.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsLocalTime.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Time(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 0) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Time");
+void CFXJSE_FormCalcContext::Time(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 0) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time");
     return;
   }
 
   time_t now;
   FXSYS_time(&now);
-
   struct tm* pGmt = gmtime(&now);
-  args.GetReturnValue()->SetInteger(
+  info.GetReturnValue().Set(
       (pGmt->tm_hour * 3600 + pGmt->tm_min * 60 + pGmt->tm_sec) * 1000);
 }
 
 // static
-void CFXJSE_FormCalcContext::Time2Num(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Time2Num(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Time2Num");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time2Num");
     return;
   }
 
   ByteString bsTime;
-  std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, timeValue.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> timeValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), timeValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  bsTime = ValueToUTF8String(timeValue.get());
+  bsTime = ValueToUTF8String(info.GetIsolate(), timeValue);
 
   ByteString bsFormat;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
-    if (ValueIsNull(pThis, formatValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> formatValue = GetSimpleValue(info, 1);
+    if (ValueIsNull(info.GetIsolate(), formatValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsFormat = ValueToUTF8String(formatValue.get());
+    bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue);
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (ValueIsNull(pThis, localeValue.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (ValueIsNull(info.GetIsolate(), localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
   CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  LocaleIface* pLocale = nullptr;
-  if (bsLocale.IsEmpty()) {
-    CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-    pLocale = pThisNode->GetLocale();
-  } else {
+  GCedLocaleIface* pLocale = nullptr;
+  if (!bsLocale.IsEmpty()) {
     pLocale =
         pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale.AsStringView()));
   }
+  if (!pLocale) {
+    CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
+    pLocale = pThisNode->GetLocale();
+  }
 
   WideString wsFormat;
-  if (bsFormat.IsEmpty())
-    wsFormat = pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
-  else
+  if (bsFormat.IsEmpty()) {
+    wsFormat =
+        pLocale->GetTimePattern(LocaleIface::DateTimeSubcategory::kDefault);
+  } else {
     wsFormat = WideString::FromUTF8(bsFormat.AsStringView());
-
+  }
   wsFormat = L"time{" + wsFormat + L"}";
-  CXFA_LocaleValue localeValue(XFA_VT_TIME,
+  CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kTime,
                                WideString::FromUTF8(bsTime.AsStringView()),
                                wsFormat, pLocale, pMgr);
   if (!localeValue.IsValid()) {
-    args.GetReturnValue()->SetInteger(0);
+    info.GetReturnValue().Set(0);
     return;
   }
 
   CFX_DateTime uniTime = localeValue.GetTime();
   int32_t hour = uniTime.GetHour();
-  int32_t min = uniTime.GetMinute();
-  int32_t second = uniTime.GetSecond();
-  int32_t milSecond = uniTime.GetMillisecond();
-  int32_t mins = hour * 60 + min;
+  int32_t minute = uniTime.GetMinute();
+  const int32_t second = uniTime.GetSecond();
+  const int32_t millisecond = uniTime.GetMillisecond();
 
-  mins -= (CXFA_TimeZoneProvider().GetTimeZone().tzHour * 60);
-  while (mins > 1440)
-    mins -= 1440;
+  constexpr int kMinutesInDay = 24 * 60;
+  int32_t minutes_with_tz =
+      hour * 60 + minute - CXFA_TimeZoneProvider().GetTimeZoneInMinutes();
+  minutes_with_tz %= kMinutesInDay;
+  if (minutes_with_tz < 0)
+    minutes_with_tz += kMinutesInDay;
 
-  while (mins < 0)
-    mins += 1440;
-
-  hour = mins / 60;
-  min = mins % 60;
-  args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
-                                    second * 1000 + milSecond + 1);
+  hour = minutes_with_tz / 60;
+  minute = minutes_with_tz % 60;
+  info.GetReturnValue().Set(hour * 3600000 + minute * 60000 + second * 1000 +
+                            millisecond + 1);
 }
 
 // static
-void CFXJSE_FormCalcContext::TimeFmt(CFXJSE_Value* pThis,
-                                     ByteStringView bsFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::TimeFmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"TimeFmt");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("TimeFmt");
     return;
   }
 
   int32_t iStyle = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
-    if (argStyle->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> infotyle = GetSimpleValue(info, 0);
+    if (fxv8::IsNull(infotyle)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
+    iStyle = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), infotyle));
     if (iStyle > 4 || iStyle < 0)
       iStyle = 0;
   }
 
   ByteString bsLocale;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> argLocale = GetSimpleValue(pThis, args, 1);
-    if (argLocale->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argLocale = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(argLocale)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(argLocale.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale);
   }
 
   ByteString bsFormat =
       GetStandardTimeFormat(pThis, iStyle, bsLocale.AsStringView());
-  args.GetReturnValue()->SetString(bsFormat.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView()));
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_HostObject* pThis,
                                                  ByteStringView bsDate,
                                                  ByteStringView bsFormat,
                                                  ByteStringView bsLocale) {
@@ -2448,21 +2548,22 @@
     return ByteString();
 
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
   if (!pLocale)
     return ByteString();
 
   WideString wsFormat = FormatFromString(pLocale, bsFormat);
-  CFX_DateTime dt = CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(bsDate),
-                                     wsFormat, pLocale, pMgr)
-                        .GetDate();
+  CFX_DateTime dt =
+      CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate,
+                       WideString::FromUTF8(bsDate), wsFormat, pLocale, pMgr)
+          .GetDate();
 
   return ByteString::Format("%4d-%02d-%02d", dt.GetYear(), dt.GetMonth(),
                             dt.GetDay());
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_HostObject* pThis,
                                                  ByteStringView bsDate,
                                                  ByteStringView bsFormat,
                                                  ByteStringView bsLocale) {
@@ -2471,19 +2572,20 @@
     return ByteString();
 
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
   if (!pLocale)
     return ByteString();
 
   WideString wsFormat = FormatFromString(pLocale, bsFormat);
   WideString wsRet;
-  CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(bsDate), pMgr)
-      .FormatPatterns(wsRet, wsFormat, pLocale, XFA_VALUEPICTURE_Display);
+  CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate,
+                   WideString::FromUTF8(bsDate), pMgr)
+      .FormatPatterns(wsRet, wsFormat, pLocale, XFA_ValuePicture::kDisplay);
   return wsRet.ToUTF8();
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_HostObject* pThis,
                                                  ByteStringView bsTime,
                                                  ByteStringView bsFormat,
                                                  ByteStringView bsLocale) {
@@ -2492,21 +2594,22 @@
     return ByteString();
 
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
-  LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
+  GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale);
   if (!pLocale)
     return ByteString();
 
   WideString wsFormat = {
       L"time{", FormatFromString(pLocale, bsFormat).AsStringView(), L"}"};
-  CXFA_LocaleValue widgetValue(XFA_VT_TIME, WideString::FromUTF8(bsTime), pMgr);
+  CXFA_LocaleValue widgetValue(CXFA_LocaleValue::ValueType::kTime,
+                               WideString::FromUTF8(bsTime), pMgr);
   WideString wsRet;
   widgetValue.FormatPatterns(wsRet, wsFormat, pLocale,
-                             XFA_VALUEPICTURE_Display);
+                             XFA_ValuePicture::kDisplay);
   return wsRet.ToUTF8();
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_HostObject* pThis,
                                                       int32_t iStyle,
                                                       ByteStringView bsLocale,
                                                       bool bStandard) {
@@ -2519,7 +2622,7 @@
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_HostObject* pThis,
                                                       int32_t iStyle,
                                                       ByteStringView bsLocale,
                                                       bool bStandard) {
@@ -2533,7 +2636,7 @@
 
 // static
 ByteString CFXJSE_FormCalcContext::GetStandardDateFormat(
-    CFXJSE_Value* pThis,
+    CFXJSE_HostObject* pThis,
     int32_t iStyle,
     ByteStringView bsLocale) {
   return GetLocalDateFormat(pThis, iStyle, bsLocale, true);
@@ -2541,14 +2644,14 @@
 
 // static
 ByteString CFXJSE_FormCalcContext::GetStandardTimeFormat(
-    CFXJSE_Value* pThis,
+    CFXJSE_HostObject* pThis,
     int32_t iStyle,
     ByteStringView bsLocale) {
   return GetLocalTimeFormat(pThis, iStyle, bsLocale, true);
 }
 
 // static
-ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_Value* pThis,
+ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_HostObject* pThis,
                                                int32_t iTime,
                                                ByteStringView bsFormat,
                                                ByteStringView bsLocale,
@@ -2577,28 +2680,29 @@
 }
 
 // static
-void CFXJSE_FormCalcContext::Apr(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Apr(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Apr");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("Apr");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double nPrincipal = ValueToDouble(pThis, argOne.get());
-  double nPayment = ValueToDouble(pThis, argTwo.get());
-  double nPeriods = ValueToDouble(pThis, argThree.get());
-  if (nPrincipal <= 0 || nPayment <= 0 || nPeriods <= 0) {
+  double nPrincipal = ValueToDouble(info.GetIsolate(), argOne);
+  double nPayment = ValueToDouble(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nPrincipal <= 0 || nPayment <= 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
@@ -2615,7 +2719,7 @@
          (r * nTemp * nPeriods * (nTemp / (1 + r)))) /
         ((nTemp - 1) * (nTemp - 1));
     if (nDerivative == 0) {
-      args.GetReturnValue()->SetNull();
+      info.GetReturnValue().SetNull();
       return;
     }
 
@@ -2626,63 +2730,65 @@
     }
     nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
   }
-  args.GetReturnValue()->SetDouble(r * 12);
+  info.GetReturnValue().Set(r * 12);
 }
 
 // static
-void CFXJSE_FormCalcContext::CTerm(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::CTerm(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"CTerm");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("CTerm");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nRate = ValueToFloat(pThis, argOne.get());
-  float nFutureValue = ValueToFloat(pThis, argTwo.get());
-  float nInitAmount = ValueToFloat(pThis, argThree.get());
+  float nRate = ValueToFloat(info.GetIsolate(), argOne);
+  float nFutureValue = ValueToFloat(info.GetIsolate(), argTwo);
+  float nInitAmount = ValueToFloat(info.GetIsolate(), argThree);
   if ((nRate <= 0) || (nFutureValue <= 0) || (nInitAmount <= 0)) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(log((float)(nFutureValue / nInitAmount)) /
-                                  log((float)(1 + nRate)));
+  info.GetReturnValue().Set(log((float)(nFutureValue / nInitAmount)) /
+                            log((float)(1 + nRate)));
 }
 
 // static
-void CFXJSE_FormCalcContext::FV(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::FV(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"FV");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("FV");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double nAmount = ValueToDouble(pThis, argOne.get());
-  double nRate = ValueToDouble(pThis, argTwo.get());
-  double nPeriod = ValueToDouble(pThis, argThree.get());
-  if ((nRate < 0) || (nPeriod <= 0) || (nAmount <= 0)) {
+  double nAmount = ValueToDouble(info.GetIsolate(), argOne);
+  double nRate = ValueToDouble(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nAmount <= 0 || nRate < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
@@ -2690,44 +2796,46 @@
   double dResult = 0;
   if (nRate) {
     double nTemp = 1;
-    for (int i = 0; i < nPeriod; ++i) {
+    for (int i = 0; i < nPeriods; ++i) {
       nTemp *= 1 + nRate;
     }
     dResult = nAmount * (nTemp - 1) / nRate;
   } else {
-    dResult = nAmount * nPeriod;
+    dResult = nAmount * nPeriods;
   }
 
-  args.GetReturnValue()->SetDouble(dResult);
+  info.GetReturnValue().Set(dResult);
 }
 
 // static
-void CFXJSE_FormCalcContext::IPmt(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::IPmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 5) {
-    pContext->ThrowParamCountMismatchException(L"IPmt");
+  if (info.Length() != 5) {
+    pContext->ThrowParamCountMismatchException("IPmt");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-  std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
-      ValueIsNull(pThis, argFive.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
+  v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree) ||
+      ValueIsNull(info.GetIsolate(), argFour) ||
+      ValueIsNull(info.GetIsolate(), argFive)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPayment = ValueToFloat(pThis, argThree.get());
-  float nFirstMonth = ValueToFloat(pThis, argFour.get());
-  float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
+  float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne);
+  float nRate = ValueToFloat(info.GetIsolate(), argTwo);
+  float nPayment = ValueToFloat(info.GetIsolate(), argThree);
+  float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour);
+  float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive);
   if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
       (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
     pContext->ThrowArgumentMismatchException();
@@ -2735,14 +2843,15 @@
   }
 
   float nRateOfMonth = nRate / 12;
-  int32_t iNums =
-      (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
-                 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
-                log10((float)(1 + nRateOfMonth)));
-  int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
+  int32_t iNums = static_cast<int32_t>(
+      (log10((float)(nPayment / nPrincipalAmount)) -
+       log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
+      log10((float)(1 + nRateOfMonth)));
+  int32_t iEnd =
+      std::min(static_cast<int32_t>(nFirstMonth + nNumberOfMonths - 1), iNums);
 
   if (nPayment < nPrincipalAmount * nRateOfMonth) {
-    args.GetReturnValue()->SetFloat(0);
+    info.GetReturnValue().Set(0);
     return;
   }
 
@@ -2755,114 +2864,113 @@
     nSum += nPrincipalAmount * nRateOfMonth;
     nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
   }
-  args.GetReturnValue()->SetFloat(nSum);
+  info.GetReturnValue().Set(nSum);
 }
 
 // static
-void CFXJSE_FormCalcContext::NPV(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::NPV(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
-  if (argc < 3) {
-    pContext->ThrowParamCountMismatchException(L"NPV");
+  int32_t argc = info.Length();
+  if (argc < 2) {
+    pContext->ThrowParamCountMismatchException("NPV");
     return;
   }
 
-  std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
-  for (int32_t i = 0; i < argc; i++) {
-    argValues.push_back(GetSimpleValue(pThis, args, i));
-    if (ValueIsNull(pThis, argValues[i].get())) {
-      args.GetReturnValue()->SetNull();
-      return;
-    }
+  v8::Local<v8::Value> argValue = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argValue)) {
+    info.GetReturnValue().SetNull();
+    return;
   }
 
-  double nRate = ValueToDouble(pThis, argValues[0].get());
+  double nRate = ValueToDouble(info.GetIsolate(), argValue);
   if (nRate <= 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  std::vector<double> data(argc - 1);
-  for (int32_t i = 1; i < argc; i++)
-    data.push_back(ValueToDouble(pThis, argValues[i].get()));
+  std::vector<double> data;
+  for (int32_t i = 1; i < argc; i++) {
+    argValue = GetSimpleValue(info, i);
+    if (ValueIsNull(info.GetIsolate(), argValue)) {
+      info.GetReturnValue().SetNull();
+      return;
+    }
+    data.push_back(ValueToDouble(info.GetIsolate(), argValue));
+  }
 
   double nSum = 0;
-  int32_t iIndex = 0;
-  for (int32_t i = 0; i < argc - 1; i++) {
-    double nTemp = 1;
-    for (int32_t j = 0; j <= i; j++)
-      nTemp *= 1 + nRate;
-
-    double nNum = data[iIndex++];
-    nSum += nNum / nTemp;
+  double nDivisor = 1.0 + nRate;
+  while (!data.empty()) {
+    nSum += data.back();
+    nSum /= nDivisor;
+    data.pop_back();
   }
-  args.GetReturnValue()->SetDouble(nSum);
+  info.GetReturnValue().Set(nSum);
 }
 
 // static
-void CFXJSE_FormCalcContext::Pmt(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Pmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Pmt");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("Pmt");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nPrincipal = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPeriods = ValueToFloat(pThis, argThree.get());
-  if ((nPrincipal <= 0) || (nRate <= 0) || (nPeriods <= 0)) {
+  double nPrincipal = ValueToDouble(info.GetIsolate(), argOne);
+  double nRate = ValueToDouble(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nPrincipal <= 0 || nRate <= 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  float nTmp = 1 + nRate;
-  float nSum = nTmp;
-  for (int32_t i = 0; i < nPeriods - 1; ++i)
-    nSum *= nTmp;
-
-  args.GetReturnValue()->SetFloat((nPrincipal * nRate * nSum) / (nSum - 1));
+  double nSum = pow(1.0 + nRate, nPeriods);
+  info.GetReturnValue().Set((nPrincipal * nRate * nSum) / (nSum - 1));
 }
 
 // static
-void CFXJSE_FormCalcContext::PPmt(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::PPmt(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 5) {
-    pContext->ThrowParamCountMismatchException(L"PPmt");
+  if (info.Length() != 5) {
+    pContext->ThrowParamCountMismatchException("PPmt");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-  std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
-      ValueIsNull(pThis, argFive.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
+  v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree) ||
+      ValueIsNull(info.GetIsolate(), argFour) ||
+      ValueIsNull(info.GetIsolate(), argFive)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nPayment = ValueToFloat(pThis, argThree.get());
-  float nFirstMonth = ValueToFloat(pThis, argFour.get());
-  float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
+  float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne);
+  float nRate = ValueToFloat(info.GetIsolate(), argTwo);
+  float nPayment = ValueToFloat(info.GetIsolate(), argThree);
+  float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour);
+  float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive);
   if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
       (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
     pContext->ThrowArgumentMismatchException();
@@ -2870,11 +2978,12 @@
   }
 
   float nRateOfMonth = nRate / 12;
-  int32_t iNums =
-      (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
-                 log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
-                log10((float)(1 + nRateOfMonth)));
-  int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
+  int32_t iNums = static_cast<int32_t>(
+      (log10((float)(nPayment / nPrincipalAmount)) -
+       log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
+      log10((float)(1 + nRateOfMonth)));
+  int32_t iEnd =
+      std::min(static_cast<int32_t>(nFirstMonth + nNumberOfMonths - 1), iNums);
   if (nPayment < nPrincipalAmount * nRateOfMonth) {
     pContext->ThrowArgumentMismatchException();
     return;
@@ -2891,126 +3000,124 @@
     nSum += nTemp;
     nPrincipalAmount -= nTemp;
   }
-  args.GetReturnValue()->SetFloat(nSum);
+  info.GetReturnValue().Set(nSum);
 }
 
 // static
-void CFXJSE_FormCalcContext::PV(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::PV(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"PV");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("PV");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double nAmount = ValueToDouble(pThis, argOne.get());
-  double nRate = ValueToDouble(pThis, argTwo.get());
-  double nPeriod = ValueToDouble(pThis, argThree.get());
-  if ((nAmount <= 0) || (nRate < 0) || (nPeriod <= 0)) {
+  double nAmount = ValueToDouble(info.GetIsolate(), argOne);
+  double nRate = ValueToDouble(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nAmount <= 0 || nRate < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  double nTemp = 1;
-  for (int32_t i = 0; i < nPeriod; ++i)
-    nTemp *= 1 + nRate;
-
-  nTemp = 1 / nTemp;
-  args.GetReturnValue()->SetDouble(nAmount * ((1 - nTemp) / nRate));
+  double nTemp = 1 / pow(1.0 + nRate, nPeriods);
+  info.GetReturnValue().Set(nAmount * ((1.0 - nTemp) / nRate));
 }
 
 // static
-void CFXJSE_FormCalcContext::Rate(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Rate(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Rate");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("Rate");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nFuture = ValueToFloat(pThis, argOne.get());
-  float nPresent = ValueToFloat(pThis, argTwo.get());
-  float nTotalNumber = ValueToFloat(pThis, argThree.get());
-  if ((nFuture <= 0) || (nPresent < 0) || (nTotalNumber <= 0)) {
+  float nFuture = ValueToFloat(info.GetIsolate(), argOne);
+  float nPresent = ValueToFloat(info.GetIsolate(), argTwo);
+  int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree);
+  if (nFuture <= 0 || nPresent < 0 || nPeriods == 0) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(
-      FXSYS_pow((float)(nFuture / nPresent), (float)(1 / nTotalNumber)) - 1);
+  info.GetReturnValue().Set(powf(nFuture / nPresent, 1.0f / nPeriods) - 1.0f);
 }
 
 // static
-void CFXJSE_FormCalcContext::Term(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Term(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 3) {
-    pContext->ThrowParamCountMismatchException(L"Term");
+  if (info.Length() != 3) {
+    pContext->ThrowParamCountMismatchException("Term");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
-      ValueIsNull(pThis, argThree.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo) ||
+      ValueIsNull(info.GetIsolate(), argThree)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float nMount = ValueToFloat(pThis, argOne.get());
-  float nRate = ValueToFloat(pThis, argTwo.get());
-  float nFuture = ValueToFloat(pThis, argThree.get());
+  float nMount = ValueToFloat(info.GetIsolate(), argOne);
+  float nRate = ValueToFloat(info.GetIsolate(), argTwo);
+  float nFuture = ValueToFloat(info.GetIsolate(), argThree);
   if ((nMount <= 0) || (nRate <= 0) || (nFuture <= 0)) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  args.GetReturnValue()->SetFloat(log((float)(nFuture / nMount * nRate) + 1) /
-                                  log((float)(1 + nRate)));
+  info.GetReturnValue().Set(log((float)(nFuture / nMount * nRate) + 1) /
+                            log((float)(1 + nRate)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Choose(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Choose(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
+  int32_t argc = info.Length();
   if (argc < 2) {
-    pContext->ThrowParamCountMismatchException(L"Choose");
+    pContext->ThrowParamCountMismatchException("Choose");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  if (ValueIsNull(info.GetIsolate(), info[0])) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  int32_t iIndex = (int32_t)ValueToFloat(pThis, argOne.get());
+  int32_t iIndex =
+      static_cast<int32_t>(ValueToFloat(info.GetIsolate(), info[0]));
   if (iIndex < 1) {
-    args.GetReturnValue()->SetString("");
+    info.GetReturnValue().SetEmptyString();
     return;
   }
 
@@ -3018,268 +3125,261 @@
   bool bStopCounterFlags = false;
   int32_t iArgIndex = 1;
   int32_t iValueIndex = 0;
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
   while (!bFound && !bStopCounterFlags && (iArgIndex < argc)) {
-    std::unique_ptr<CFXJSE_Value> argIndexValue = args.GetValue(iArgIndex);
-    if (argIndexValue->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argIndexValue->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
+    v8::Local<v8::Value> argIndexValue = info[iArgIndex];
+    if (fxv8::IsArray(argIndexValue)) {
+      v8::Local<v8::Array> arr = argIndexValue.As<v8::Array>();
+      uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
       if (iLength > 3)
         bStopCounterFlags = true;
 
       iValueIndex += (iLength - 2);
       if (iValueIndex >= iIndex) {
-        auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-        argIndexValue->GetObjectPropertyByIdx(1, propertyValue.get());
-        argIndexValue->GetObjectPropertyByIdx(
-            (iLength - 1) - (iValueIndex - iIndex), jsObjectValue.get());
-        if (propertyValue->IsNull()) {
-          GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-        } else {
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), newPropertyValue.get());
+        v8::Local<v8::Value> propertyValue =
+            fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
+        v8::Local<v8::Value> jsValue = fxv8::ReentrantGetArrayElementHelper(
+            info.GetIsolate(), arr, (iLength - 1) - (iValueIndex - iIndex));
+        v8::Local<v8::Value> newPropertyValue;
+        if (fxv8::IsObject(jsValue)) {
+          v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+          if (fxv8::IsNull(propertyValue)) {
+            newPropertyValue =
+                GetObjectDefaultValue(info.GetIsolate(), jsObjectValue);
+          } else {
+            ByteString bsName = fxv8::ReentrantToByteStringHelper(
+                info.GetIsolate(), propertyValue);
+            newPropertyValue = fxv8::ReentrantGetObjectPropertyHelper(
+                info.GetIsolate(), jsObjectValue, bsName.AsStringView());
+          }
         }
-        ByteString bsChosen = ValueToUTF8String(newPropertyValue.get());
-        args.GetReturnValue()->SetString(bsChosen.AsStringView());
+        ByteString bsChosen =
+            ValueToUTF8String(info.GetIsolate(), newPropertyValue);
+        info.GetReturnValue().Set(
+            fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView()));
         bFound = true;
       }
     } else {
       iValueIndex++;
       if (iValueIndex == iIndex) {
-        ByteString bsChosen = ValueToUTF8String(argIndexValue.get());
-        args.GetReturnValue()->SetString(bsChosen.AsStringView());
+        ByteString bsChosen =
+            ValueToUTF8String(info.GetIsolate(), argIndexValue);
+        info.GetReturnValue().Set(
+            fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView()));
         bFound = true;
       }
     }
     iArgIndex++;
   }
   if (!bFound)
-    args.GetReturnValue()->SetString("");
+    info.GetReturnValue().SetEmptyString();
 }
 
 // static
-void CFXJSE_FormCalcContext::Exists(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Exists");
+void CFXJSE_FormCalcContext::Exists(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Exists");
     return;
   }
-  args.GetReturnValue()->SetInteger(args.GetValue(0)->IsObject());
+  info.GetReturnValue().Set(fxv8::IsObject(info[0]));
 }
 
 // static
-void CFXJSE_FormCalcContext::HasValue(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"HasValue");
+void CFXJSE_FormCalcContext::HasValue(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("HasValue");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (!argOne->IsString()) {
-    args.GetReturnValue()->SetInteger(argOne->IsNumber() ||
-                                      argOne->IsBoolean());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (!fxv8::IsString(argOne)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(fxv8::IsNumber(argOne) || fxv8::IsBoolean(argOne)));
     return;
   }
 
-  ByteString bsValue = argOne->ToString();
+  ByteString bsValue =
+      fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argOne);
   bsValue.TrimLeft();
-  args.GetReturnValue()->SetInteger(!bsValue.IsEmpty());
+  info.GetReturnValue().Set(static_cast<int>(!bsValue.IsEmpty()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Oneof(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() < 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Oneof");
+void CFXJSE_FormCalcContext::Oneof(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() < 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Oneof");
     return;
   }
 
-  bool bFlags = false;
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::vector<std::unique_ptr<CFXJSE_Value>> parameterValues =
-      unfoldArgs(pThis, args);
-  for (const auto& value : parameterValues) {
-    if (simpleValueCompare(pThis, argOne.get(), value.get())) {
-      bFlags = true;
-      break;
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  for (const auto& value : UnfoldArgs(info)) {
+    if (SimpleValueCompare(info.GetIsolate(), argOne, value)) {
+      info.GetReturnValue().Set(1);
+      return;
     }
   }
-
-  args.GetReturnValue()->SetInteger(bFlags);
+  info.GetReturnValue().Set(0);
 }
 
 // static
-void CFXJSE_FormCalcContext::Within(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Within");
+void CFXJSE_FormCalcContext::Within(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Within");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetUndefined();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetUndefined();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argLow = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> argHigh = GetSimpleValue(pThis, args, 2);
-  if (argOne->IsNumber()) {
-    float oneNumber = ValueToFloat(pThis, argOne.get());
-    float lowNumber = ValueToFloat(pThis, argLow.get());
-    float heightNumber = ValueToFloat(pThis, argHigh.get());
-    args.GetReturnValue()->SetInteger((oneNumber >= lowNumber) &&
-                                      (oneNumber <= heightNumber));
+  v8::Local<v8::Value> argLow = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> argHigh = GetSimpleValue(info, 2);
+  if (fxv8::IsNumber(argOne)) {
+    float oneNumber = ValueToFloat(info.GetIsolate(), argOne);
+    float lowNumber = ValueToFloat(info.GetIsolate(), argLow);
+    float heightNumber = ValueToFloat(info.GetIsolate(), argHigh);
+    info.GetReturnValue().Set(static_cast<int>((oneNumber >= lowNumber) &&
+                                               (oneNumber <= heightNumber)));
     return;
   }
 
-  ByteString bsOne = ValueToUTF8String(argOne.get());
-  ByteString bsLow = ValueToUTF8String(argLow.get());
-  ByteString bsHeight = ValueToUTF8String(argHigh.get());
-  args.GetReturnValue()->SetInteger(
-      (bsOne.Compare(bsLow.AsStringView()) >= 0) &&
-      (bsOne.Compare(bsHeight.AsStringView()) <= 0));
+  ByteString bsOne = ValueToUTF8String(info.GetIsolate(), argOne);
+  ByteString bsLow = ValueToUTF8String(info.GetIsolate(), argLow);
+  ByteString bsHeight = ValueToUTF8String(info.GetIsolate(), argHigh);
+  info.GetReturnValue().Set(
+      static_cast<int>((bsOne.Compare(bsLow.AsStringView()) >= 0) &&
+                       (bsOne.Compare(bsHeight.AsStringView()) <= 0)));
 }
 
 // static
-void CFXJSE_FormCalcContext::If(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"If");
+void CFXJSE_FormCalcContext::If(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("If");
     return;
   }
 
-  args.GetReturnValue()->Assign(GetSimpleValue(pThis, args, 0)->ToBoolean()
-                                    ? GetSimpleValue(pThis, args, 1).get()
-                                    : GetSimpleValue(pThis, args, 2).get());
+  const bool condition = fxv8::ReentrantToBooleanHelper(
+      info.GetIsolate(), GetSimpleValue(info, 0));
+
+  info.GetReturnValue().Set(GetSimpleValue(info, condition ? 1 : 2));
 }
 
 // static
-void CFXJSE_FormCalcContext::Eval(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Eval(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Eval");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("Eval");
     return;
   }
 
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  std::unique_ptr<CFXJSE_Value> scriptValue = GetSimpleValue(pThis, args, 0);
-  ByteString bsUtf8Script = ValueToUTF8String(scriptValue.get());
+  v8::Isolate* pIsolate = pContext->GetIsolate();
+  v8::Local<v8::Value> scriptValue = GetSimpleValue(info, 0);
+  ByteString bsUtf8Script = ValueToUTF8String(info.GetIsolate(), scriptValue);
   if (bsUtf8Script.IsEmpty()) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  CFX_WideTextBuf wsJavaScriptBuf;
-  if (!CFXJSE_FormCalcContext::Translate(
-          WideString::FromUTF8(bsUtf8Script.AsStringView()).AsStringView(),
-          &wsJavaScriptBuf)) {
+  WideString wsCalcScript = WideString::FromUTF8(bsUtf8Script.AsStringView());
+  absl::optional<WideTextBuffer> wsJavaScriptBuf =
+      CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(),
+                                        wsCalcScript.AsStringView());
+  if (!wsJavaScriptBuf.has_value()) {
     pContext->ThrowCompilerErrorException();
     return;
   }
+  std::unique_ptr<CFXJSE_Context> pNewContext =
+      CFXJSE_Context::Create(pIsolate, nullptr, nullptr, nullptr);
 
-  std::unique_ptr<CFXJSE_Context> pNewContext(
-      CFXJSE_Context::Create(pIsolate, nullptr, nullptr));
+  auto returnValue = std::make_unique<CFXJSE_Value>();
+  ByteString bsScript = FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView());
+  pNewContext->ExecuteScript(bsScript.AsStringView(), returnValue.get(),
+                             v8::Local<v8::Object>());
 
-  auto returnValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  pNewContext->ExecuteScript(
-      FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).c_str(), returnValue.get(),
-      nullptr);
-
-  args.GetReturnValue()->Assign(returnValue.get());
+  info.GetReturnValue().Set(returnValue->DirectGetValue());
 }
 
 // static
-void CFXJSE_FormCalcContext::Ref(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Ref(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Ref");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("Ref");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray() && !argOne->IsObject() && !argOne->IsBoolean() &&
-      !argOne->IsString() && !argOne->IsNull() && !argOne->IsNumber()) {
+  v8::Local<v8::Value> argOne = info[0];
+  if (fxv8::IsBoolean(argOne) || fxv8::IsString(argOne) ||
+      fxv8::IsNumber(argOne)) {
+    info.GetReturnValue().Set(argOne);
+    return;
+  }
+
+  std::vector<v8::Local<v8::Value>> values(3);
+  int intVal = 3;
+  if (fxv8::IsNull(argOne)) {
+    // TODO(dsinclair): Why is this 4 when the others are all 3?
+    intVal = 4;
+    values[2] = fxv8::NewNullHelper(info.GetIsolate());
+  } else if (fxv8::IsArray(argOne)) {
+    v8::Local<v8::Array> arr = argOne.As<v8::Array>();
+    v8::Local<v8::Value> propertyValue =
+        fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
+    v8::Local<v8::Value> jsObjectValue =
+        fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
+    if (!fxv8::IsNull(propertyValue) || fxv8::IsNull(jsObjectValue)) {
+      pContext->ThrowArgumentMismatchException();
+      return;
+    }
+    values[2] = jsObjectValue;
+  } else if (fxv8::IsObject(argOne)) {
+    values[2] = argOne;
+  } else {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  if (argOne->IsBoolean() || argOne->IsString() || argOne->IsNumber()) {
-    args.GetReturnValue()->Assign(argOne.get());
-    return;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> values;
-  for (int32_t i = 0; i < 3; i++)
-    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int intVal = 3;
-  if (argOne->IsNull()) {
-    // TODO(dsinclair): Why is this 4 when the others are all 3?
-    intVal = 4;
-    values[2]->SetNull();
-  } else if (argOne->IsArray()) {
-#ifndef NDEBUG
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectProperty("length", lengthValue.get());
-    ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectPropertyByIdx(1, propertyValue.get());
-    argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (!propertyValue->IsNull() || jsObjectValue->IsNull()) {
-      pContext->ThrowArgumentMismatchException();
-      return;
-    }
-
-    values[2]->Assign(jsObjectValue.get());
-  } else if (argOne->IsObject()) {
-    values[2]->Assign(argOne.get());
-  }
-
-  values[0]->SetInteger(intVal);
-  values[1]->SetNull();
-  args.GetReturnValue()->SetArray(values);
+  values[0] = fxv8::NewNumberHelper(info.GetIsolate(), intVal);
+  values[1] = fxv8::NewNullHelper(info.GetIsolate());
+  info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values));
 }
 
 // static
-void CFXJSE_FormCalcContext::UnitType(CFXJSE_Value* pThis,
-                                      ByteStringView bsFuncName,
-                                      CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"UnitType");
+void CFXJSE_FormCalcContext::UnitType(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitType");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
-  if (unitspanValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> unitspanValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(unitspanValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsUnitspan = ValueToUTF8String(unitspanValue.get());
+  ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue);
   if (bsUnitspan.IsEmpty()) {
-    args.GetReturnValue()->SetString("in");
+    info.GetReturnValue().SetEmptyString();
     return;
   }
 
-  enum XFA_FM2JS_VALUETYPE_ParserStatus {
+  enum XFA_FormCalc_VALUETYPE_ParserStatus {
     VALUETYPE_START,
     VALUETYPE_HAVEINVALIDCHAR,
     VALUETYPE_HAVEDIGIT,
@@ -3298,7 +3398,7 @@
   while (IsWhitespace(pData[u]))
     u++;
 
-  XFA_FM2JS_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START;
+  XFA_FormCalc_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START;
   wchar_t typeChar;
   // TODO(dsinclair): Cleanup this parser, figure out what the various checks
   //    are for.
@@ -3348,43 +3448,43 @@
   }
   switch (eParserStatus) {
     case VALUETYPE_ISCM:
-      args.GetReturnValue()->SetString("cm");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "cm"));
       break;
     case VALUETYPE_ISMM:
-      args.GetReturnValue()->SetString("mm");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "mm"));
       break;
     case VALUETYPE_ISPT:
-      args.GetReturnValue()->SetString("pt");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "pt"));
       break;
     case VALUETYPE_ISMP:
-      args.GetReturnValue()->SetString("mp");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "mp"));
       break;
     default:
-      args.GetReturnValue()->SetString("in");
+      info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "in"));
       break;
   }
 }
 
 // static
-void CFXJSE_FormCalcContext::UnitValue(CFXJSE_Value* pThis,
-                                       ByteStringView bsFuncName,
-                                       CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::UnitValue(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"UnitValue");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitValue");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
-  if (unitspanValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> unitspanValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(unitspanValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsUnitspan = ValueToUTF8String(unitspanValue.get());
+  ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue);
   const char* pData = bsUnitspan.c_str();
   if (!pData) {
-    args.GetReturnValue()->SetInteger(0);
+    info.GetReturnValue().Set(0);
     return;
   }
 
@@ -3416,15 +3516,15 @@
 
   ByteString bsUnit;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> unitValue = GetSimpleValue(pThis, args, 1);
-    ByteString bsUnitTemp = ValueToUTF8String(unitValue.get());
+    v8::Local<v8::Value> unitValue = GetSimpleValue(info, 1);
+    ByteString bsUnitTemp = ValueToUTF8String(info.GetIsolate(), unitValue);
     const char* pChar = bsUnitTemp.c_str();
     size_t uVal = 0;
     while (IsWhitespace(pChar[uVal]))
       ++uVal;
 
     while (uVal < bsUnitTemp.GetLength()) {
-      if (!std::isdigit(pChar[uVal]) && pChar[uVal] != '.')
+      if (!isdigit(pChar[uVal]) && pChar[uVal] != '.')
         break;
       ++uVal;
     }
@@ -3501,99 +3601,102 @@
     else
       dResult = dFirstNumber / 72000;
   }
-  args.GetReturnValue()->SetDouble(dResult);
+  info.GetReturnValue().Set(dResult);
 }
 
 // static
-void CFXJSE_FormCalcContext::At(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"At");
+void CFXJSE_FormCalcContext::At(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("At");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString stringTwo = ValueToUTF8String(argTwo.get());
+  ByteString stringTwo = ValueToUTF8String(info.GetIsolate(), argTwo);
   if (stringTwo.IsEmpty()) {
-    args.GetReturnValue()->SetInteger(1);
+    info.GetReturnValue().Set(1);
     return;
   }
 
-  ByteString stringOne = ValueToUTF8String(argOne.get());
+  ByteString stringOne = ValueToUTF8String(info.GetIsolate(), argOne);
   auto pos = stringOne.Find(stringTwo.AsStringView());
-  args.GetReturnValue()->SetInteger(pos.has_value() ? pos.value() + 1 : 0);
+  info.GetReturnValue().Set(
+      static_cast<int>(pos.has_value() ? pos.value() + 1 : 0));
 }
 
 // static
-void CFXJSE_FormCalcContext::Concat(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Concat(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Concat");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Concat");
     return;
   }
 
   ByteString bsResult;
   bool bAllNull = true;
   for (int32_t i = 0; i < argc; i++) {
-    std::unique_ptr<CFXJSE_Value> value = GetSimpleValue(pThis, args, i);
-    if (ValueIsNull(pThis, value.get()))
+    v8::Local<v8::Value> value = GetSimpleValue(info, i);
+    if (ValueIsNull(info.GetIsolate(), value))
       continue;
 
     bAllNull = false;
-    bsResult += ValueToUTF8String(value.get());
+    bsResult += ValueToUTF8String(info.GetIsolate(), value);
   }
 
   if (bAllNull) {
-    args.GetReturnValue()->SetNull();
+    info.GetReturnValue().SetNull();
     return;
   }
-
-  args.GetReturnValue()->SetString(bsResult.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Decode(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Decode(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Decode");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Decode");
     return;
   }
 
   if (argc == 1) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    if (ValueIsNull(pThis, argOne.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+    if (ValueIsNull(info.GetIsolate(), argOne)) {
+      info.GetReturnValue().SetNull();
       return;
     }
 
-    WideString decoded = DecodeURL(
-        WideString::FromUTF8(ValueToUTF8String(argOne.get()).AsStringView()));
-
-    args.GetReturnValue()->SetString(
-        FX_UTF8Encode(decoded.AsStringView()).AsStringView());
+    WideString decoded = DecodeURL(WideString::FromUTF8(
+        ValueToUTF8String(info.GetIsolate(), argOne).AsStringView()));
+    auto result = FX_UTF8Encode(decoded.AsStringView());
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsToDecode = ValueToUTF8String(argOne.get());
-  ByteString bsIdentify = ValueToUTF8String(argTwo.get());
+  ByteString bsToDecode = ValueToUTF8String(info.GetIsolate(), argOne);
+  ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo);
   WideString decoded;
 
   WideString wsToDecode = WideString::FromUTF8(bsToDecode.AsStringView());
@@ -3605,42 +3708,45 @@
   else
     decoded = DecodeURL(wsToDecode);
 
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(decoded.AsStringView()).AsStringView());
+  auto result = FX_UTF8Encode(decoded.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Encode(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Encode(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Encode");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Encode");
     return;
   }
 
   if (argc == 1) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    if (ValueIsNull(pThis, argOne.get())) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+    if (ValueIsNull(info.GetIsolate(), argOne)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-
-    WideString encoded = EncodeURL(ValueToUTF8String(argOne.get()));
-    args.GetReturnValue()->SetString(
-        FX_UTF8Encode(encoded.AsStringView()).AsStringView());
+    WideString encoded =
+        EncodeURL(ValueToUTF8String(info.GetIsolate(), argOne));
+    auto result = FX_UTF8Encode(encoded.AsStringView());
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if (ValueIsNull(info.GetIsolate(), argOne) ||
+      ValueIsNull(info.GetIsolate(), argTwo)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsToEncode = ValueToUTF8String(argOne.get());
-  ByteString bsIdentify = ValueToUTF8String(argTwo.get());
+  ByteString bsToEncode = ValueToUTF8String(info.GetIsolate(), argOne);
+  ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo);
   WideString encoded;
   if (bsIdentify.EqualNoCase("html"))
     encoded = EncodeHTML(bsToEncode);
@@ -3649,42 +3755,43 @@
   else
     encoded = EncodeURL(bsToEncode);
 
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(encoded.AsStringView()).AsStringView());
+  auto result = FX_UTF8Encode(encoded.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Format(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Format(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() < 2) {
-    pContext->ThrowParamCountMismatchException(L"Format");
+  if (info.Length() < 2) {
+    pContext->ThrowParamCountMismatchException("Format");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsPattern = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne);
 
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString bsValue = ValueToUTF8String(argTwo.get());
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo);
 
   CXFA_Document* pDoc = pContext->GetDocument();
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
   CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-  LocaleIface* pLocale = pThisNode->GetLocale();
+  GCedLocaleIface* pLocale = pThisNode->GetLocale();
   WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView());
   WideString wsValue = WideString::FromUTF8(bsValue.AsStringView());
   bool bPatternIsString;
-  uint32_t dwPatternType;
+  CXFA_LocaleValue::ValueType dwPatternType;
   std::tie(bPatternIsString, dwPatternType) =
       PatternStringType(bsPattern.AsStringView());
   if (!bPatternIsString) {
     switch (dwPatternType) {
-      case XFA_VT_DATETIME: {
+      case CXFA_LocaleValue::ValueType::kDateTime: {
         auto iTChar = wsPattern.Find(L'T');
         if (!iTChar.has_value()) {
-          args.GetReturnValue()->SetString("");
+          info.GetReturnValue().SetEmptyString();
           return;
         }
         WideString wsDatePattern(L"date{");
@@ -3695,28 +3802,28 @@
             wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}";
         wsPattern = wsDatePattern + wsTimePattern;
       } break;
-      case XFA_VT_DATE: {
+      case CXFA_LocaleValue::ValueType::kDate: {
         wsPattern = L"date{" + wsPattern + L"}";
       } break;
-      case XFA_VT_TIME: {
+      case CXFA_LocaleValue::ValueType::kTime: {
         wsPattern = L"time{" + wsPattern + L"}";
       } break;
-      case XFA_VT_TEXT: {
+      case CXFA_LocaleValue::ValueType::kText: {
         wsPattern = L"text{" + wsPattern + L"}";
       } break;
-      case XFA_VT_FLOAT: {
+      case CXFA_LocaleValue::ValueType::kFloat: {
         wsPattern = L"num{" + wsPattern + L"}";
       } break;
       default: {
         WideString wsTestPattern = L"num{" + wsPattern + L"}";
-        CXFA_LocaleValue tempLocaleValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
-                                         pLocale, pMgr);
+        CXFA_LocaleValue tempLocaleValue(CXFA_LocaleValue::ValueType::kFloat,
+                                         wsValue, wsTestPattern, pLocale, pMgr);
         if (tempLocaleValue.IsValid()) {
           wsPattern = std::move(wsTestPattern);
-          dwPatternType = XFA_VT_FLOAT;
+          dwPatternType = CXFA_LocaleValue::ValueType::kFloat;
         } else {
           wsPattern = L"text{" + wsPattern + L"}";
-          dwPatternType = XFA_VT_TEXT;
+          dwPatternType = CXFA_LocaleValue::ValueType::kText;
         }
       } break;
     }
@@ -3725,73 +3832,74 @@
                                pMgr);
   WideString wsRet;
   if (!localeValue.FormatPatterns(wsRet, wsPattern, pLocale,
-                                  XFA_VALUEPICTURE_Display)) {
-    args.GetReturnValue()->SetString("");
+                                  XFA_ValuePicture::kDisplay)) {
+    info.GetReturnValue().SetEmptyString();
     return;
   }
-
-  args.GetReturnValue()->SetString(wsRet.ToUTF8().AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), wsRet.ToUTF8().AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Left(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Left");
+void CFXJSE_FormCalcContext::Left(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Left");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if ((ValueIsNull(pThis, argOne.get())) ||
-      (ValueIsNull(pThis, argTwo.get()))) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if ((ValueIsNull(info.GetIsolate(), argOne)) ||
+      (ValueIsNull(info.GetIsolate(), argTwo))) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
-  int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
-  args.GetReturnValue()->SetString(bsSource.First(count).AsStringView());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
+  int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo));
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), bsSource.First(count).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Len(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Len");
+void CFXJSE_FormCalcContext::Len(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Len");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
-  args.GetReturnValue()->SetInteger(bsSource.GetLength());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
+  info.GetReturnValue().Set(static_cast<int>(bsSource.GetLength()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Lower(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Lower(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Lower");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Lower");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  CFX_WideTextBuf szLowBuf;
-  ByteString bsArg = ValueToUTF8String(argOne.get());
+  WideTextBuffer szLowBuf;
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
   WideString wsArg = WideString::FromUTF8(bsArg.AsStringView());
   for (wchar_t ch : wsArg) {
     if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0xC0 && ch <= 0xDE))
@@ -3800,78 +3908,79 @@
       ch += 1;
     szLowBuf.AppendChar(ch);
   }
-  szLowBuf.AppendChar(0);
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(szLowBuf.AsStringView()).AsStringView());
+  auto result = FX_UTF8Encode(szLowBuf.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Ltrim(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Ltrim");
+void CFXJSE_FormCalcContext::Ltrim(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ltrim");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
   bsSource.TrimLeft();
-  args.GetReturnValue()->SetString(bsSource.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Parse(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Parse(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 2) {
-    pContext->ThrowParamCountMismatchException(L"Parse");
+  if (info.Length() != 2) {
+    pContext->ThrowParamCountMismatchException("Parse");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if (ValueIsNull(pThis, argTwo.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if (ValueIsNull(info.GetIsolate(), argTwo)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsPattern = ValueToUTF8String(argOne.get());
-  ByteString bsValue = ValueToUTF8String(argTwo.get());
+  ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne);
+  ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo);
   CXFA_Document* pDoc = pContext->GetDocument();
   CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr();
   CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
-  LocaleIface* pLocale = pThisNode->GetLocale();
+  GCedLocaleIface* pLocale = pThisNode->GetLocale();
   WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView());
   WideString wsValue = WideString::FromUTF8(bsValue.AsStringView());
   bool bPatternIsString;
-  uint32_t dwPatternType;
+  CXFA_LocaleValue::ValueType dwPatternType;
   std::tie(bPatternIsString, dwPatternType) =
       PatternStringType(bsPattern.AsStringView());
   if (bPatternIsString) {
     CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
                                  pMgr);
     if (!localeValue.IsValid()) {
-      args.GetReturnValue()->SetString("");
+      info.GetReturnValue().SetEmptyString();
       return;
     }
-    args.GetReturnValue()->SetString(
-        localeValue.GetValue().ToUTF8().AsStringView());
+    auto result = localeValue.GetValue().ToUTF8();
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
     return;
   }
 
   switch (dwPatternType) {
-    case XFA_VT_DATETIME: {
+    case CXFA_LocaleValue::ValueType::kDateTime: {
       auto iTChar = wsPattern.Find(L'T');
       if (!iTChar.has_value()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
       WideString wsDatePattern(L"date{" + wsPattern.First(iTChar.value()) +
@@ -3883,243 +3992,228 @@
       CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
                                    pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().ToUTF8().AsStringView());
+      auto result = localeValue.GetValue().ToUTF8();
+      info.GetReturnValue().Set(
+          fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
       return;
     }
-    case XFA_VT_DATE: {
+    case CXFA_LocaleValue::ValueType::kDate: {
       wsPattern = L"date{" + wsPattern + L"}";
       CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
                                    pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().ToUTF8().AsStringView());
+      auto result = localeValue.GetValue().ToUTF8();
+      info.GetReturnValue().Set(
+          fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
       return;
     }
-    case XFA_VT_TIME: {
+    case CXFA_LocaleValue::ValueType::kTime: {
       wsPattern = L"time{" + wsPattern + L"}";
       CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale,
                                    pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().ToUTF8().AsStringView());
+      auto result = localeValue.GetValue().ToUTF8();
+      info.GetReturnValue().Set(
+          fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
       return;
     }
-    case XFA_VT_TEXT: {
+    case CXFA_LocaleValue::ValueType::kText: {
       wsPattern = L"text{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsPattern, pLocale,
-                                   pMgr);
+      CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText, wsValue,
+                                   wsPattern, pLocale, pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetString(
-          localeValue.GetValue().ToUTF8().AsStringView());
+      auto result = localeValue.GetValue().ToUTF8();
+      info.GetReturnValue().Set(
+          fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
       return;
     }
-    case XFA_VT_FLOAT: {
+    case CXFA_LocaleValue::ValueType::kFloat: {
       wsPattern = L"num{" + wsPattern + L"}";
-      CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsPattern, pLocale,
-                                   pMgr);
+      CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat, wsValue,
+                                   wsPattern, pLocale, pMgr);
       if (!localeValue.IsValid()) {
-        args.GetReturnValue()->SetString("");
+        info.GetReturnValue().SetEmptyString();
         return;
       }
-      args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
+      info.GetReturnValue().Set(localeValue.GetDoubleNum());
       return;
     }
     default: {
       {
         WideString wsTestPattern = L"num{" + wsPattern + L"}";
-        CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
-                                     pLocale, pMgr);
+        CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat,
+                                     wsValue, wsTestPattern, pLocale, pMgr);
         if (localeValue.IsValid()) {
-          args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
+          info.GetReturnValue().Set(localeValue.GetDoubleNum());
           return;
         }
       }
 
       {
         WideString wsTestPattern = L"text{" + wsPattern + L"}";
-        CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsTestPattern,
-                                     pLocale, pMgr);
+        CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText,
+                                     wsValue, wsTestPattern, pLocale, pMgr);
         if (localeValue.IsValid()) {
-          args.GetReturnValue()->SetString(
-              localeValue.GetValue().ToUTF8().AsStringView());
+          auto result = localeValue.GetValue().ToUTF8();
+          info.GetReturnValue().Set(
+              fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView()));
           return;
         }
       }
-      args.GetReturnValue()->SetString("");
+      info.GetReturnValue().SetEmptyString();
       return;
     }
   }
 }
 
 // static
-void CFXJSE_FormCalcContext::Replace(CFXJSE_Value* pThis,
-                                     ByteStringView bsFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Replace(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 2 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Replace");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Replace");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
   ByteString bsOne;
   ByteString bsTwo;
-  if (!ValueIsNull(pThis, argOne.get()) && !ValueIsNull(pThis, argTwo.get())) {
-    bsOne = ValueToUTF8String(argOne.get());
-    bsTwo = ValueToUTF8String(argTwo.get());
+  if (!ValueIsNull(info.GetIsolate(), argOne) &&
+      !ValueIsNull(info.GetIsolate(), argTwo)) {
+    bsOne = ValueToUTF8String(info.GetIsolate(), argOne);
+    bsTwo = ValueToUTF8String(info.GetIsolate(), argTwo);
   }
 
   ByteString bsThree;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    bsThree = ValueToUTF8String(argThree.get());
+    v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+    bsThree = ValueToUTF8String(info.GetIsolate(), argThree);
   }
 
-  size_t iFindLen = bsTwo.GetLength();
-  std::ostringstream szResult;
-  size_t iFindIndex = 0;
-  for (size_t u = 0; u < bsOne.GetLength(); ++u) {
-    char ch = static_cast<char>(bsOne[u]);
-    if (ch != static_cast<char>(bsTwo[iFindIndex])) {
-      szResult << ch;
-      continue;
-    }
-
-    size_t iTemp = u + 1;
-    ++iFindIndex;
-    while (iFindIndex < iFindLen) {
-      uint8_t chTemp = bsOne[iTemp];
-      if (chTemp != bsTwo[iFindIndex]) {
-        iFindIndex = 0;
-        break;
-      }
-
-      ++iTemp;
-      ++iFindIndex;
-    }
-    if (iFindIndex == iFindLen) {
-      szResult << bsThree;
-      u += iFindLen - 1;
-      iFindIndex = 0;
-    } else {
-      szResult << ch;
-    }
-  }
-  szResult << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(szResult.str().c_str()));
+  bsOne.Replace(bsTwo.AsStringView(), bsThree.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsOne.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Right(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Right");
+void CFXJSE_FormCalcContext::Right(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Right");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  if ((ValueIsNull(pThis, argOne.get())) ||
-      (ValueIsNull(pThis, argTwo.get()))) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  if ((ValueIsNull(info.GetIsolate(), argOne)) ||
+      (ValueIsNull(info.GetIsolate(), argTwo))) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
-  int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
-  args.GetReturnValue()->SetString(bsSource.Last(count).AsStringView());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
+  int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo));
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), bsSource.Last(count).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Rtrim(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Rtrim");
+void CFXJSE_FormCalcContext::Rtrim(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Rtrim");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(argOne.get());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne);
   bsSource.TrimRight();
-  args.GetReturnValue()->SetString(bsSource.AsStringView());
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Space(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Space");
+void CFXJSE_FormCalcContext::Space(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Space");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  int32_t count = std::max(0, ValueToInteger(pThis, argOne.get()));
-  std::ostringstream spaceString;
-  int32_t index = 0;
-  while (index < count) {
-    spaceString << ' ';
-    index++;
+  int count = std::max(0, ValueToInteger(info.GetIsolate(), argOne));
+  if (count > kMaxCharCount) {
+    ToFormCalcContext(pThis)->ThrowException("String too long.");
+    return;
   }
-  spaceString << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(spaceString.str().c_str()));
+  DataVector<char> space_string(count, ' ');
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(space_string)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Str(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Str");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Str");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
-  if (numberValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> numberValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(numberValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  float fNumber = ValueToFloat(pThis, numberValue.get());
+  float fNumber = ValueToFloat(info.GetIsolate(), numberValue);
 
-  int32_t iWidth = 10;
+  constexpr int32_t kDefaultWidth = 10;
+  int32_t iWidth = kDefaultWidth;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> widthValue = GetSimpleValue(pThis, args, 1);
-    iWidth = static_cast<int32_t>(ValueToFloat(pThis, widthValue.get()));
+    v8::Local<v8::Value> widthValue = GetSimpleValue(info, 1);
+    iWidth = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), widthValue));
+    if (iWidth > kMaxCharCount) {
+      ToFormCalcContext(pThis)->ThrowException("String too long.");
+      return;
+    }
   }
 
-  int32_t iPrecision = 0;
+  constexpr int32_t kDefaultPrecision = 0;
+  int32_t iPrecision = kDefaultPrecision;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> precisionValue =
-        GetSimpleValue(pThis, args, 2);
-    iPrecision = std::max(
-        0, static_cast<int32_t>(ValueToFloat(pThis, precisionValue.get())));
+    constexpr int32_t kMaxPrecision = 15;
+    v8::Local<v8::Value> precision_value = GetSimpleValue(info, 2);
+    iPrecision = std::max(0, static_cast<int32_t>(ValueToFloat(
+                                 info.GetIsolate(), precision_value)));
+    iPrecision = std::min(iPrecision, kMaxPrecision);
   }
 
   ByteString bsFormat = "%";
@@ -4140,34 +4234,31 @@
     ++u;
   }
 
-  std::ostringstream resultBuf;
   if (u > iWidth || (iPrecision + u) >= iWidth) {
-    int32_t i = 0;
-    while (i < iWidth) {
-      resultBuf << '*';
-      ++i;
-    }
-    resultBuf << '\0';
-    args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+    DataVector<char> stars(std::max(iWidth, 0), '*');
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(stars)));
     return;
   }
 
+  ByteString resultBuf;
   if (u == iLength) {
     if (iLength > iWidth) {
       int32_t i = 0;
       while (i < iWidth) {
-        resultBuf << '*';
+        resultBuf += '*';
         ++i;
       }
     } else {
       int32_t i = 0;
       while (i < iWidth - iLength) {
-        resultBuf << ' ';
+        resultBuf += ' ';
         ++i;
       }
-      resultBuf << pData;
+      resultBuf += pData;
     }
-    args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+    info.GetReturnValue().Set(
+        fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView()));
     return;
   }
 
@@ -4177,16 +4268,16 @@
 
   int32_t i = 0;
   while (i < iLeavingSpace) {
-    resultBuf << ' ';
+    resultBuf += ' ';
     ++i;
   }
   i = 0;
   while (i < u) {
-    resultBuf << pData[i];
+    resultBuf += pData[i];
     ++i;
   }
   if (iPrecision != 0)
-    resultBuf << '.';
+    resultBuf += '.';
 
   u++;
   i = 0;
@@ -4194,221 +4285,213 @@
     if (i >= iPrecision)
       break;
 
-    resultBuf << pData[u];
+    resultBuf += pData[u];
     ++i;
     ++u;
   }
   while (i < iPrecision) {
-    resultBuf << '0';
+    resultBuf += '0';
     ++i;
   }
-  resultBuf << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Stuff(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Stuff(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 3 || argc > 4) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Stuff");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Stuff");
     return;
   }
 
-  ByteString bsSource;
-  ByteString bsInsert;
-  int32_t iLength = 0;
-  int32_t iStart = 0;
+  v8::Local<v8::Value> sourceValue = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> startValue = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> deleteValue = GetSimpleValue(info, 2);
+  if (fxv8::IsNull(sourceValue) || fxv8::IsNull(startValue) ||
+      fxv8::IsNull(deleteValue)) {
+    info.GetReturnValue().SetNull();
+    return;
+  }
+
+  int32_t iStart = 1;  // one-based character indexing.
   int32_t iDelete = 0;
-  std::unique_ptr<CFXJSE_Value> sourceValue = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> startValue = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> deleteValue = GetSimpleValue(pThis, args, 2);
-  if (!sourceValue->IsNull() && !startValue->IsNull() &&
-      !deleteValue->IsNull()) {
-    bsSource = ValueToUTF8String(sourceValue.get());
-    iLength = bsSource.GetLength();
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), sourceValue);
+  int32_t iLength = pdfium::base::checked_cast<int32_t>(bsSource.GetLength());
+  if (iLength) {
     iStart = pdfium::clamp(
-        static_cast<int32_t>(ValueToFloat(pThis, startValue.get())), 1,
+        static_cast<int32_t>(ValueToFloat(info.GetIsolate(), startValue)), 1,
         iLength);
-    iDelete = std::max(
-        0, static_cast<int32_t>(ValueToFloat(pThis, deleteValue.get())));
+    iDelete = pdfium::clamp(
+        static_cast<int32_t>(ValueToFloat(info.GetIsolate(), deleteValue)), 0,
+        iLength - iStart + 1);
   }
 
+  ByteString bsInsert;
   if (argc > 3) {
-    std::unique_ptr<CFXJSE_Value> insertValue = GetSimpleValue(pThis, args, 3);
-    bsInsert = ValueToUTF8String(insertValue.get());
+    v8::Local<v8::Value> insertValue = GetSimpleValue(info, 3);
+    bsInsert = ValueToUTF8String(info.GetIsolate(), insertValue);
   }
 
-  --iStart;
-  std::ostringstream szResult;
-  int32_t i = 0;
-  while (i < iStart) {
-    szResult << static_cast<char>(bsSource[i]);
-    ++i;
-  }
-  szResult << bsInsert.AsStringView();
-  i = iStart + iDelete;
-  while (i < iLength) {
-    szResult << static_cast<char>(bsSource[i]);
-    ++i;
-  }
-  szResult << '\0';
-  args.GetReturnValue()->SetString(ByteStringView(szResult.str().c_str()));
+  --iStart;  // now zero-based.
+  ByteString bsResult = {bsSource.AsStringView().First(iStart),
+                         bsInsert.AsStringView(),
+                         bsSource.AsStringView().Substr(iStart + iDelete)};
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Substr(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args) {
-  if (args.GetLength() != 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Substr");
+void CFXJSE_FormCalcContext::Substr(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 3) {
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Substr");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> string_value = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> start_value = GetSimpleValue(pThis, args, 1);
-  std::unique_ptr<CFXJSE_Value> end_value = GetSimpleValue(pThis, args, 2);
-  if (ValueIsNull(pThis, string_value.get()) ||
-      ValueIsNull(pThis, start_value.get()) ||
-      ValueIsNull(pThis, end_value.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> string_value = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> start_value = GetSimpleValue(info, 1);
+  v8::Local<v8::Value> end_value = GetSimpleValue(info, 2);
+  if (ValueIsNull(info.GetIsolate(), string_value) ||
+      ValueIsNull(info.GetIsolate(), start_value) ||
+      ValueIsNull(info.GetIsolate(), end_value)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  ByteString bsSource = ValueToUTF8String(string_value.get());
+  ByteString bsSource = ValueToUTF8String(info.GetIsolate(), string_value);
   size_t iLength = bsSource.GetLength();
   if (iLength == 0) {
-    args.GetReturnValue()->SetString("");
+    info.GetReturnValue().SetEmptyString();
     return;
   }
 
   // |start_value| is 1-based. Assume first character if |start_value| is less
   // than 1, per spec. Subtract 1 since |iStart| is 0-based.
-  size_t iStart = std::max(ValueToInteger(pThis, start_value.get()), 1) - 1;
+  size_t iStart =
+      std::max(ValueToInteger(info.GetIsolate(), start_value), 1) - 1;
   if (iStart >= iLength) {
-    args.GetReturnValue()->SetString("");
+    info.GetReturnValue().SetEmptyString();
     return;
   }
 
   // Negative values are treated as 0. Can't clamp() due to sign mismatches.
-  size_t iCount = std::max(ValueToInteger(pThis, end_value.get()), 0);
+  size_t iCount = std::max(ValueToInteger(info.GetIsolate(), end_value), 0);
   iCount = std::min(iCount, iLength - iStart);
-  args.GetReturnValue()->SetString(
-      bsSource.Substr(iStart, iCount).AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), bsSource.Substr(iStart, iCount).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Uuid(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Uuid(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 0 || argc > 1) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Uuid");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Uuid");
     return;
   }
 
   int32_t iNum = 0;
   if (argc > 0) {
-    std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-    iNum = static_cast<int32_t>(ValueToFloat(pThis, argOne.get()));
+    v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+    iNum = static_cast<int32_t>(ValueToFloat(info.GetIsolate(), argOne));
   }
-  args.GetReturnValue()->SetString(GUIDString(!!iNum).AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), GUIDString(!!iNum).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Upper(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::Upper(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 2) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Upper");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Upper");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (ValueIsNull(pThis, argOne.get())) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (ValueIsNull(info.GetIsolate(), argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  CFX_WideTextBuf upperStringBuf;
-  ByteString bsArg = ValueToUTF8String(argOne.get());
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
   WideString wsArg = WideString::FromUTF8(bsArg.AsStringView());
-  const wchar_t* pData = wsArg.c_str();
-  size_t i = 0;
-  while (i < wsArg.GetLength()) {
-    int32_t ch = pData[i];
+  WideString upperStringBuf;
+  upperStringBuf.Reserve(wsArg.GetLength());
+  for (wchar_t ch : wsArg) {
     if ((ch >= 0x61 && ch <= 0x7A) || (ch >= 0xE0 && ch <= 0xFE))
       ch -= 32;
     else if (ch == 0x101 || ch == 0x103 || ch == 0x105)
       ch -= 1;
 
-    upperStringBuf.AppendChar(ch);
-    ++i;
+    upperStringBuf += ch;
   }
-  upperStringBuf.AppendChar(0);
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(),
+      FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::WordNum(CFXJSE_Value* pThis,
-                                     ByteStringView bsFuncName,
-                                     CFXJSE_Arguments& args) {
-  int32_t argc = args.GetLength();
+void CFXJSE_FormCalcContext::WordNum(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  int32_t argc = info.Length();
   if (argc < 1 || argc > 3) {
-    ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"WordNum");
+    ToFormCalcContext(pThis)->ThrowParamCountMismatchException("WordNum");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
-  if (numberValue->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> numberValue = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(numberValue)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  float fNumber = ValueToFloat(pThis, numberValue.get());
+  float fNumber = ValueToFloat(info.GetIsolate(), numberValue);
 
   int32_t iIdentifier = 0;
   if (argc > 1) {
-    std::unique_ptr<CFXJSE_Value> identifierValue =
-        GetSimpleValue(pThis, args, 1);
-    if (identifierValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> identifierValue = GetSimpleValue(info, 1);
+    if (fxv8::IsNull(identifierValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
     iIdentifier =
-        static_cast<int32_t>(ValueToFloat(pThis, identifierValue.get()));
+        static_cast<int32_t>(ValueToFloat(info.GetIsolate(), identifierValue));
   }
 
   ByteString bsLocale;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
-    if (localeValue->IsNull()) {
-      args.GetReturnValue()->SetNull();
+    v8::Local<v8::Value> localeValue = GetSimpleValue(info, 2);
+    if (fxv8::IsNull(localeValue)) {
+      info.GetReturnValue().SetNull();
       return;
     }
-    bsLocale = ValueToUTF8String(localeValue.get());
+    bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue);
   }
 
-  if (std::isnan(fNumber) || fNumber < 0.0f ||
-      fNumber > 922337203685477550.0f) {
-    args.GetReturnValue()->SetString("*");
+  if (isnan(fNumber) || fNumber < 0.0f || fNumber > 922337203685477550.0f) {
+    info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "*"));
     return;
   }
-
-  args.GetReturnValue()->SetString(
-      WordUS(ByteString::Format("%.2f", fNumber), iIdentifier).AsStringView());
+  ByteString bsFormatted = ByteString::Format("%.2f", fNumber);
+  ByteString bsWorded = WordUS(bsFormatted.AsStringView(), iIdentifier);
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), bsWorded.AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Get(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Get(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Get");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("Get");
     return;
   }
 
@@ -4416,31 +4499,34 @@
   if (!pDoc)
     return;
 
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
   if (!pAppProvider)
     return;
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsUrl = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsUrl = ValueToUTF8String(info.GetIsolate(), argOne);
   RetainPtr<IFX_SeekableReadStream> pFile =
       pAppProvider->DownloadURL(WideString::FromUTF8(bsUrl.AsStringView()));
   if (!pFile)
     return;
 
-  int32_t size = pFile->GetSize();
-  std::vector<uint8_t> dataBuf(size);
-  pFile->ReadBlock(dataBuf.data(), size);
-  args.GetReturnValue()->SetString(ByteStringView(dataBuf));
+  FX_FILESIZE size = pFile->GetSize();
+  DataVector<uint8_t> dataBuf(size);
+
+  // TODO(tsepez): check return value?
+  (void)pFile->ReadBlock(dataBuf);
+  info.GetReturnValue().Set(
+      fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(dataBuf)));
 }
 
 // static
-void CFXJSE_FormCalcContext::Post(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Post(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
+  int32_t argc = info.Length();
   if (argc < 2 || argc > 5) {
-    pContext->ThrowParamCountMismatchException(L"Post");
+    pContext->ThrowParamCountMismatchException("Post");
     return;
   }
 
@@ -4448,32 +4534,32 @@
   if (!pDoc)
     return;
 
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
   if (!pAppProvider)
     return;
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsURL = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne);
 
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString bsData = ValueToUTF8String(argTwo.get());
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo);
 
   ByteString bsContentType;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    bsContentType = ValueToUTF8String(argThree.get());
+    v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+    bsContentType = ValueToUTF8String(info.GetIsolate(), argThree);
   }
 
   ByteString bsEncode;
   if (argc > 3) {
-    std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
-    bsEncode = ValueToUTF8String(argFour.get());
+    v8::Local<v8::Value> argFour = GetSimpleValue(info, 3);
+    bsEncode = ValueToUTF8String(info.GetIsolate(), argFour);
   }
 
   ByteString bsHeader;
   if (argc > 4) {
-    std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
-    bsHeader = ValueToUTF8String(argFive.get());
+    v8::Local<v8::Value> argFive = GetSimpleValue(info, 4);
+    bsHeader = ValueToUTF8String(info.GetIsolate(), argFive);
   }
 
   WideString decodedResponse;
@@ -4486,17 +4572,18 @@
     pContext->ThrowServerDeniedException();
     return;
   }
-  args.GetReturnValue()->SetString(decodedResponse.ToUTF8().AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(), decodedResponse.ToUTF8().AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::Put(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::Put(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  int32_t argc = args.GetLength();
+  int32_t argc = info.Length();
   if (argc < 2 || argc > 3) {
-    pContext->ThrowParamCountMismatchException(L"Put");
+    pContext->ThrowParamCountMismatchException("Put");
     return;
   }
 
@@ -4504,22 +4591,21 @@
   if (!pDoc)
     return;
 
-  IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
+  CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider();
   if (!pAppProvider)
     return;
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsURL = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne);
 
-  std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
-  ByteString bsData = ValueToUTF8String(argTwo.get());
+  v8::Local<v8::Value> argTwo = GetSimpleValue(info, 1);
+  ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo);
 
   ByteString bsEncode;
   if (argc > 2) {
-    std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
-    bsEncode = ValueToUTF8String(argThree.get());
+    v8::Local<v8::Value> argThree = GetSimpleValue(info, 2);
+    bsEncode = ValueToUTF8String(info.GetIsolate(), argThree);
   }
-
   if (!pAppProvider->PutRequestURL(
           WideString::FromUTF8(bsURL.AsStringView()),
           WideString::FromUTF8(bsData.AsStringView()),
@@ -4527,869 +4613,641 @@
     pContext->ThrowServerDeniedException();
     return;
   }
-
-  args.GetReturnValue()->SetString("");
+  info.GetReturnValue().SetEmptyString();
 }
 
 // static
-void CFXJSE_FormCalcContext::assign_value_operator(CFXJSE_Value* pThis,
-                                                   ByteStringView bsFuncName,
-                                                   CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::assign_value_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* pIsolate = info.GetIsolate();
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 2) {
+  if (info.Length() != 2) {
     pContext->ThrowCompilerErrorException();
     return;
   }
-
-  std::unique_ptr<CFXJSE_Value> lValue = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> rValue = GetSimpleValue(pThis, args, 1);
-  if (lValue->IsArray()) {
-    v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-    auto leftLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    lValue->GetObjectProperty("length", leftLengthValue.get());
-    int32_t iLeftLength = leftLengthValue->ToInteger();
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    lValue->GetObjectPropertyByIdx(1, propertyValue.get());
-    if (propertyValue->IsNull()) {
-      for (int32_t i = 2; i < iLeftLength; i++) {
-        lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
-        if (!SetObjectDefaultValue(jsObjectValue.get(), rValue.get())) {
+  ByteStringView bsFuncName("asgn_val_op");
+  v8::Local<v8::Value> lValue = info[0];
+  v8::Local<v8::Value> rValue = GetSimpleValue(info, 1);
+  if (fxv8::IsArray(lValue)) {
+    v8::Local<v8::Array> arr = lValue.As<v8::Array>();
+    uint32_t iLeftLength = fxv8::GetArrayLengthHelper(arr);
+    v8::Local<v8::Value> propertyValue =
+        fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
+    for (uint32_t i = 2; i < iLeftLength; i++) {
+      v8::Local<v8::Value> jsValue =
+          fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, i);
+      if (!fxv8::IsObject(jsValue)) {
+        pContext->ThrowNoDefaultPropertyException(bsFuncName);
+        return;
+      }
+      v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+      if (fxv8::IsNull(propertyValue)) {
+        if (!SetObjectDefaultValue(pIsolate, jsObjectValue, rValue)) {
           pContext->ThrowNoDefaultPropertyException(bsFuncName);
           return;
         }
-      }
-    } else {
-      for (int32_t i = 2; i < iLeftLength; i++) {
-        lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
-        jsObjectValue->SetObjectProperty(
-            propertyValue->ToString().AsStringView(), rValue.get());
+      } else {
+        fxv8::ReentrantPutObjectPropertyHelper(
+            pIsolate, jsObjectValue,
+            fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue)
+                .AsStringView(),
+            rValue);
       }
     }
-  } else if (lValue->IsObject()) {
-    if (!SetObjectDefaultValue(lValue.get(), rValue.get())) {
+  } else if (fxv8::IsObject(lValue)) {
+    if (!SetObjectDefaultValue(pIsolate, lValue.As<v8::Object>(), rValue)) {
       pContext->ThrowNoDefaultPropertyException(bsFuncName);
       return;
     }
   }
-  args.GetReturnValue()->Assign(rValue.get());
+  info.GetReturnValue().Set(rValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::logical_or_operator(CFXJSE_Value* pThis,
-                                                 ByteStringView bsFuncName,
-                                                 CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::logical_or_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float first = ValueToFloat(pThis, argFirst.get());
-  float second = ValueToFloat(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first || second) ? 1 : 0);
+  float first = ValueToFloat(info.GetIsolate(), argFirst);
+  float second = ValueToFloat(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first || second));
 }
 
 // static
-void CFXJSE_FormCalcContext::logical_and_operator(CFXJSE_Value* pThis,
-                                                  ByteStringView bsFuncName,
-                                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::logical_and_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  float first = ValueToFloat(pThis, argFirst.get());
-  float second = ValueToFloat(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first && second) ? 1 : 0);
+  float first = ValueToFloat(info.GetIsolate(), argFirst);
+  float second = ValueToFloat(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first && second));
 }
 
 // static
-void CFXJSE_FormCalcContext::equality_operator(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::equality_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  if (fm_ref_equal(pThis, args)) {
-    args.GetReturnValue()->SetInteger(1);
+  if (fm_ref_equal(pThis, info)) {
+    info.GetReturnValue().Set(1);
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    args.GetReturnValue()->SetInteger(argFirst->ToString() ==
-                                      argSecond->ToString());
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    info.GetReturnValue().Set(static_cast<int>(
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) ==
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond)));
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first == second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first == second));
 }
 
 // static
-void CFXJSE_FormCalcContext::notequality_operator(CFXJSE_Value* pThis,
-                                                  ByteStringView bsFuncName,
-                                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::notequality_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  if (fm_ref_equal(pThis, args)) {
-    args.GetReturnValue()->SetInteger(0);
+  if (fm_ref_equal(pThis, info)) {
+    info.GetReturnValue().Set(0);
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 0 : 1);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(!fxv8::IsNull(argFirst) || !fxv8::IsNull(argSecond)));
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    args.GetReturnValue()->SetInteger(argFirst->ToString() !=
-                                      argSecond->ToString());
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    info.GetReturnValue().Set(static_cast<int>(
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) !=
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond)));
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger(first != second);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first != second));
 }
 
 // static
-bool CFXJSE_FormCalcContext::fm_ref_equal(CFXJSE_Value* pThis,
-                                          CFXJSE_Arguments& args) {
-  std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
-  if (!argFirst->IsArray() || !argSecond->IsArray())
+bool CFXJSE_FormCalcContext::fm_ref_equal(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Value> argFirst = info[0];
+  v8::Local<v8::Value> argSecond = info[1];
+  if (!fxv8::IsArray(argFirst) || !fxv8::IsArray(argSecond))
     return false;
 
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  auto firstFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto secondFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argFirst->GetObjectPropertyByIdx(0, firstFlagValue.get());
-  argSecond->GetObjectPropertyByIdx(0, secondFlagValue.get());
-  if (firstFlagValue->ToInteger() != 3 || secondFlagValue->ToInteger() != 3)
+  v8::Local<v8::Array> firstArr = argFirst.As<v8::Array>();
+  v8::Local<v8::Array> secondArr = argSecond.As<v8::Array>();
+  v8::Local<v8::Value> firstFlag =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 0);
+  v8::Local<v8::Value> secondFlag =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 0);
+  if (fxv8::ReentrantToInt32Helper(info.GetIsolate(), firstFlag) != 3 ||
+      fxv8::ReentrantToInt32Helper(info.GetIsolate(), secondFlag) != 3) {
+    return false;
+  }
+
+  v8::Local<v8::Value> firstValue =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 2);
+  v8::Local<v8::Value> secondValue =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 2);
+
+  if (fxv8::IsNull(firstValue) || fxv8::IsNull(secondValue))
     return false;
 
-  auto firstJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto secondJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argFirst->GetObjectPropertyByIdx(2, firstJSObject.get());
-  argSecond->GetObjectPropertyByIdx(2, secondJSObject.get());
-  if (firstJSObject->IsNull() || secondJSObject->IsNull())
-    return false;
-
-  return firstJSObject->ToHostObject() == secondJSObject->ToHostObject();
+  return FXJSE_RetrieveObjectBinding(firstValue) ==
+         FXJSE_RetrieveObjectBinding(secondValue);
 }
 
 // static
-void CFXJSE_FormCalcContext::less_operator(CFXJSE_Value* pThis,
-                                           ByteStringView bsFuncName,
-                                           CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::less_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(0);
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) < 0;
-    args.GetReturnValue()->SetInteger(result);
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    ByteString bs1 =
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
+    ByteString bs2 =
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
+    info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) < 0);
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first < second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first < second));
 }
 
 // static
-void CFXJSE_FormCalcContext::lessequal_operator(CFXJSE_Value* pThis,
-                                                ByteStringView bsFuncName,
-                                                CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::lessequal_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) <= 0;
-    args.GetReturnValue()->SetInteger(result);
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
+    auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
+    info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) <= 0);
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first <= second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first <= second));
 }
 
 // static
-void CFXJSE_FormCalcContext::greater_operator(CFXJSE_Value* pThis,
-                                              ByteStringView bsFuncName,
-                                              CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::greater_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(0);
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) > 0;
-    args.GetReturnValue()->SetInteger(result);
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
+    auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
+    info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) > 0);
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first > second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first > second));
 }
 
 // static
-void CFXJSE_FormCalcContext::greaterequal_operator(CFXJSE_Value* pThis,
-                                                   ByteStringView bsFuncName,
-                                                   CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::greaterequal_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() || argSecond->IsNull()) {
-    args.GetReturnValue()->SetInteger(
-        (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().Set(
+        static_cast<int>(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)));
     return;
   }
 
-  if (argFirst->IsString() && argSecond->IsString()) {
-    int result =
-        argFirst->ToString().Compare(argSecond->ToString().AsStringView()) >= 0;
-    args.GetReturnValue()->SetInteger(result);
+  if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) {
+    auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst);
+    auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond);
+    info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) >= 0);
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetInteger((first >= second) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(static_cast<int>(first >= second));
 }
 
 // static
-void CFXJSE_FormCalcContext::plus_operator(CFXJSE_Value* pThis,
-                                           ByteStringView bsFuncName,
-                                           CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::plus_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
-  std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
-  if (ValueIsNull(pThis, argFirst.get()) &&
-      ValueIsNull(pThis, argSecond.get())) {
-    args.GetReturnValue()->SetNull();
+  if (ValueIsNull(info.GetIsolate(), info[0]) &&
+      ValueIsNull(info.GetIsolate(), info[1])) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first + second);
+  const double first = ValueToDouble(info.GetIsolate(), info[0]);
+  const double second = ValueToDouble(info.GetIsolate(), info[1]);
+  info.GetReturnValue().Set(first + second);
 }
 
 // static
-void CFXJSE_FormCalcContext::minus_operator(CFXJSE_Value* pThis,
-                                            ByteStringView bsFuncName,
-                                            CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::minus_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first - second);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(first - second);
 }
 
 // static
-void CFXJSE_FormCalcContext::multiple_operator(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 2) {
+void CFXJSE_FormCalcContext::multiple_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 2) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  double second = ValueToDouble(pThis, argSecond.get());
-  args.GetReturnValue()->SetDouble(first * second);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
+  info.GetReturnValue().Set(first * second);
 }
 
 // static
-void CFXJSE_FormCalcContext::divide_operator(CFXJSE_Value* pThis,
-                                             ByteStringView bsFuncName,
-                                             CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::divide_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 2) {
+  if (info.Length() != 2) {
     pContext->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
-  std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
-  if (argFirst->IsNull() && argSecond->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argFirst = GetSimpleValue(info, 0);
+  v8::Local<v8::Value> argSecond = GetSimpleValue(info, 1);
+  if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double second = ValueToDouble(pThis, argSecond.get());
+  double second = ValueToDouble(info.GetIsolate(), argSecond);
   if (second == 0.0) {
     pContext->ThrowDivideByZeroException();
     return;
   }
 
-  double first = ValueToDouble(pThis, argFirst.get());
-  args.GetReturnValue()->SetDouble(first / second);
+  double first = ValueToDouble(info.GetIsolate(), argFirst);
+  info.GetReturnValue().Set(first / second);
 }
 
 // static
-void CFXJSE_FormCalcContext::positive_operator(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
+void CFXJSE_FormCalcContext::positive_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  args.GetReturnValue()->SetDouble(0.0 + ValueToDouble(pThis, argOne.get()));
+  info.GetReturnValue().Set(0.0 + ValueToDouble(info.GetIsolate(), argOne));
 }
 
 // static
-void CFXJSE_FormCalcContext::negative_operator(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
+void CFXJSE_FormCalcContext::negative_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
-  args.GetReturnValue()->SetDouble(0.0 - ValueToDouble(pThis, argOne.get()));
+  info.GetReturnValue().Set(0.0 - ValueToDouble(info.GetIsolate(), argOne));
 }
 
 // static
-void CFXJSE_FormCalcContext::logical_not_operator(CFXJSE_Value* pThis,
-                                                  ByteStringView bsFuncName,
-                                                  CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
+void CFXJSE_FormCalcContext::logical_not_operator(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  if (argOne->IsNull()) {
-    args.GetReturnValue()->SetNull();
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  if (fxv8::IsNull(argOne)) {
+    info.GetReturnValue().SetNull();
     return;
   }
 
-  double first = ValueToDouble(pThis, argOne.get());
-  args.GetReturnValue()->SetInteger((first == 0.0) ? 1 : 0);
+  double first = ValueToDouble(info.GetIsolate(), argOne);
+  info.GetReturnValue().Set((first == 0.0) ? 1 : 0);
 }
 
 // static
-void CFXJSE_FormCalcContext::dot_accessor(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  DotAccessorCommon(pThis, bsFuncName, args, /*bDotAccessor=*/true);
+void CFXJSE_FormCalcContext::dot_accessor(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  DotAccessorCommon(pThis, info, /*bDotAccessor=*/true);
 }
 
 // static
-void CFXJSE_FormCalcContext::dotdot_accessor(CFXJSE_Value* pThis,
-                                             ByteStringView bsFuncName,
-                                             CFXJSE_Arguments& args) {
-  DotAccessorCommon(pThis, bsFuncName, args, /*bDotAccessor=*/false);
+void CFXJSE_FormCalcContext::dotdot_accessor(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  DotAccessorCommon(pThis, info, /*bDotAccessor=*/false);
 }
 
 // static
-void CFXJSE_FormCalcContext::eval_translation(CFXJSE_Value* pThis,
-                                              ByteStringView bsFuncName,
-                                              CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::eval_translation(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
-    pContext->ThrowParamCountMismatchException(L"Eval");
+  if (info.Length() != 1) {
+    pContext->ThrowParamCountMismatchException("Eval");
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
-  ByteString bsArg = ValueToUTF8String(argOne.get());
+  v8::Local<v8::Value> argOne = GetSimpleValue(info, 0);
+  ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne);
   if (bsArg.IsEmpty()) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  WideString wsScript = WideString::FromUTF8(bsArg.AsStringView());
-  CFX_WideTextBuf wsJavaScriptBuf;
-  if (!CFXJSE_FormCalcContext::Translate(wsScript.AsStringView(),
-                                         &wsJavaScriptBuf)) {
+  WideString wsCalcScript = WideString::FromUTF8(bsArg.AsStringView());
+  absl::optional<WideTextBuffer> wsJavaScriptBuf =
+      CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(),
+                                        wsCalcScript.AsStringView());
+  if (!wsJavaScriptBuf.has_value()) {
     pContext->ThrowCompilerErrorException();
     return;
   }
-
-  args.GetReturnValue()->SetString(
-      FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).AsStringView());
+  info.GetReturnValue().Set(fxv8::NewStringHelper(
+      info.GetIsolate(),
+      FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView()).AsStringView()));
 }
 
 // static
-void CFXJSE_FormCalcContext::is_fm_object(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    args.GetReturnValue()->SetBoolean(false);
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  args.GetReturnValue()->SetBoolean(argOne->IsObject());
+void CFXJSE_FormCalcContext::is_fm_object(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  const bool result = info.Length() == 1 && fxv8::IsObject(info[0]);
+  info.GetReturnValue().Set(result);
 }
 
 // static
-void CFXJSE_FormCalcContext::is_fm_array(CFXJSE_Value* pThis,
-                                         ByteStringView bsFuncName,
-                                         CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
-    args.GetReturnValue()->SetBoolean(false);
-    return;
-  }
-
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  args.GetReturnValue()->SetBoolean(argOne->IsArray());
+void CFXJSE_FormCalcContext::is_fm_array(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  const bool result = info.Length() == 1 && fxv8::IsArray(info[0]);
+  info.GetReturnValue().Set(result);
 }
 
 // static
-void CFXJSE_FormCalcContext::get_fm_value(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::get_fm_value(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
+  if (info.Length() != 1) {
     pContext->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (argOne->IsArray()) {
-    v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argOne->GetObjectPropertyByIdx(1, propertyValue.get());
-    argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), args.GetReturnValue());
+  v8::Local<v8::Value> argOne = info[0];
+  if (fxv8::IsArray(argOne)) {
+    v8::Local<v8::Array> arr = argOne.As<v8::Array>();
+    v8::Local<v8::Value> propertyValue =
+        fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1);
+    v8::Local<v8::Value> jsValue =
+        fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
+    if (!fxv8::IsObject(jsValue)) {
+      info.GetReturnValue().Set(fxv8::NewUndefinedHelper(info.GetIsolate()));
       return;
     }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     args.GetReturnValue());
+    v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+    if (fxv8::IsNull(propertyValue)) {
+      info.GetReturnValue().Set(
+          GetObjectDefaultValue(info.GetIsolate(), jsObjectValue));
+      return;
+    }
+    ByteString bsName =
+        fxv8::ReentrantToByteStringHelper(info.GetIsolate(), propertyValue);
+    info.GetReturnValue().Set(fxv8::ReentrantGetObjectPropertyHelper(
+        info.GetIsolate(), jsObjectValue, bsName.AsStringView()));
     return;
   }
 
-  if (argOne->IsObject()) {
-    GetObjectDefaultValue(argOne.get(), args.GetReturnValue());
+  if (fxv8::IsObject(argOne)) {
+    v8::Local<v8::Object> obj = argOne.As<v8::Object>();
+    info.GetReturnValue().Set(GetObjectDefaultValue(info.GetIsolate(), obj));
     return;
   }
 
-  args.GetReturnValue()->Assign(argOne.get());
+  info.GetReturnValue().Set(argOne);
 }
 
 // static
-void CFXJSE_FormCalcContext::get_fm_jsobj(CFXJSE_Value* pThis,
-                                          ByteStringView bsFuncName,
-                                          CFXJSE_Arguments& args) {
-  if (args.GetLength() != 1) {
+void CFXJSE_FormCalcContext::get_fm_jsobj(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 1) {
     ToFormCalcContext(pThis)->ThrowCompilerErrorException();
     return;
   }
 
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray()) {
-    args.GetReturnValue()->Assign(argOne.get());
+  v8::Local<v8::Value> argOne = info[0];
+  if (!fxv8::IsArray(argOne)) {
+    info.GetReturnValue().Set(argOne);
     return;
   }
 
-#ifndef NDEBUG
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectProperty("length", lengthValue.get());
-  ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-  argOne->GetObjectPropertyByIdx(2, args.GetReturnValue());
+  v8::Local<v8::Array> arr = argOne.As<v8::Array>();
+  info.GetReturnValue().Set(
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2));
 }
 
 // static
-void CFXJSE_FormCalcContext::fm_var_filter(CFXJSE_Value* pThis,
-                                           ByteStringView bsFuncName,
-                                           CFXJSE_Arguments& args) {
+void CFXJSE_FormCalcContext::fm_var_filter(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  if (args.GetLength() != 1) {
+  if (info.Length() != 1) {
     pContext->ThrowCompilerErrorException();
     return;
   }
 
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
-  if (!argOne->IsArray()) {
-    std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
-    args.GetReturnValue()->Assign(simpleValue.get());
+  v8::Local<v8::Value> argOne = info[0];
+  if (!fxv8::IsArray(argOne)) {
+    info.GetReturnValue().Set(GetSimpleValue(info, 0));
     return;
   }
 
-#ifndef NDEBUG
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectProperty("length", lengthValue.get());
-  ASSERT(lengthValue->ToInteger() >= 3);
-#endif
-
-  auto flagsValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectPropertyByIdx(0, flagsValue.get());
-  int32_t iFlags = flagsValue->ToInteger();
+  v8::Local<v8::Array> arr = argOne.As<v8::Array>();
+  v8::Local<v8::Value> flagsValue =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 0);
+  int32_t iFlags = fxv8::ReentrantToInt32Helper(info.GetIsolate(), flagsValue);
   if (iFlags != 3 && iFlags != 4) {
-    std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
-    args.GetReturnValue()->Assign(simpleValue.get());
+    info.GetReturnValue().Set(GetSimpleValue(info, 0));
     return;
   }
 
   if (iFlags == 4) {
-    std::vector<std::unique_ptr<CFXJSE_Value>> values;
-    for (int32_t i = 0; i < 3; i++)
-      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-    values[0]->SetInteger(3);
-    values[1]->SetNull();
-    values[2]->SetNull();
-    args.GetReturnValue()->SetArray(values);
+    std::vector<v8::Local<v8::Value>> values(3);
+    values[0] = fxv8::NewNumberHelper(info.GetIsolate(), 3);
+    values[1] = fxv8::NewNullHelper(info.GetIsolate());
+    values[2] = fxv8::NewNullHelper(info.GetIsolate());
+    info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values));
     return;
   }
 
-  auto objectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  argOne->GetObjectPropertyByIdx(2, objectValue.get());
-  if (objectValue->IsNull()) {
+  v8::Local<v8::Value> objectValue =
+      fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2);
+  if (fxv8::IsNull(objectValue)) {
     pContext->ThrowCompilerErrorException();
     return;
   }
-  args.GetReturnValue()->Assign(argOne.get());
+  info.GetReturnValue().Set(argOne);
 }
 
 // static
-void CFXJSE_FormCalcContext::concat_fm_object(CFXJSE_Value* pThis,
-                                              ByteStringView bsFuncName,
-                                              CFXJSE_Arguments& args) {
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  uint32_t iLength = 0;
-  int32_t argc = args.GetLength();
-  std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
-  for (int32_t i = 0; i < argc; i++) {
-    argValues.push_back(args.GetValue(i));
-    if (argValues[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValues[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t length = lengthValue->ToInteger();
-      iLength = iLength + ((length > 2) ? (length - 2) : 0);
-    }
-    ++iLength;
-  }
-
-  std::vector<std::unique_ptr<CFXJSE_Value>> returnValues;
-  for (int32_t i = 0; i < (int32_t)iLength; i++)
-    returnValues.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int32_t index = 0;
-  for (int32_t i = 0; i < argc; i++) {
-    if (argValues[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argValues[i]->GetObjectProperty("length", lengthValue.get());
-
-      int32_t length = lengthValue->ToInteger();
-      for (int32_t j = 2; j < length; j++) {
-        argValues[i]->GetObjectPropertyByIdx(j, returnValues[index].get());
-        index++;
+void CFXJSE_FormCalcContext::concat_fm_object(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetIsolate();
+  std::vector<v8::Local<v8::Value>> returnValues;
+  for (int i = 0; i < info.Length(); ++i) {
+    if (fxv8::IsArray(info[i])) {
+      v8::Local<v8::Array> arr = info[i].As<v8::Array>();
+      uint32_t length = fxv8::GetArrayLengthHelper(arr);
+      for (uint32_t j = 2; j < length; j++) {
+        returnValues.push_back(
+            fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, j));
       }
     }
-    returnValues[index]->Assign(argValues[i].get());
-    index++;
+    returnValues.push_back(info[i]);
   }
-  args.GetReturnValue()->SetArray(returnValues);
-}
-
-// static
-std::unique_ptr<CFXJSE_Value> CFXJSE_FormCalcContext::GetSimpleValue(
-    CFXJSE_Value* pThis,
-    CFXJSE_Arguments& args,
-    uint32_t index) {
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  ASSERT(index < (uint32_t)args.GetLength());
-
-  std::unique_ptr<CFXJSE_Value> argIndex = args.GetValue(index);
-  if (!argIndex->IsArray() && !argIndex->IsObject())
-    return argIndex;
-
-  if (argIndex->IsArray()) {
-    auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argIndex->GetObjectProperty("length", lengthValue.get());
-    int32_t iLength = lengthValue->ToInteger();
-    auto simpleValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    if (iLength < 3) {
-      simpleValue.get()->SetUndefined();
-      return simpleValue;
-    }
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argIndex->GetObjectPropertyByIdx(1, propertyValue.get());
-    argIndex->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), simpleValue.get());
-      return simpleValue;
-    }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     simpleValue.get());
-    return simpleValue;
-  }
-
-  auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  GetObjectDefaultValue(argIndex.get(), defaultValue.get());
-  return defaultValue;
-}
-
-// static
-bool CFXJSE_FormCalcContext::ValueIsNull(CFXJSE_Value* pThis,
-                                         CFXJSE_Value* arg) {
-  if (!arg || arg->IsNull())
-    return true;
-
-  if (!arg->IsArray() && !arg->IsObject())
-    return false;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    int32_t iLength = hvalue_get_array_length(pThis, arg);
-    if (iLength < 3)
-      return true;
-
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      GetObjectDefaultValue(jsObjectValue.get(), defaultValue.get());
-      return defaultValue->IsNull();
-    }
-
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return newPropertyValue->IsNull();
-  }
-
-  auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  GetObjectDefaultValue(arg, defaultValue.get());
-  return defaultValue->IsNull();
-}
-
-// static
-int32_t CFXJSE_FormCalcContext::hvalue_get_array_length(CFXJSE_Value* pThis,
-                                                        CFXJSE_Value* arg) {
-  if (!arg || !arg->IsArray())
-    return 0;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  arg->GetObjectProperty("length", lengthValue.get());
-  return lengthValue->ToInteger();
-}
-
-// static
-bool CFXJSE_FormCalcContext::simpleValueCompare(CFXJSE_Value* pThis,
-                                                CFXJSE_Value* firstValue,
-                                                CFXJSE_Value* secondValue) {
-  if (!firstValue)
-    return false;
-
-  if (firstValue->IsString()) {
-    ByteString bsFirst = ValueToUTF8String(firstValue);
-    ByteString bsSecond = ValueToUTF8String(secondValue);
-    return bsFirst == bsSecond;
-  }
-  if (firstValue->IsNumber()) {
-    float first = ValueToFloat(pThis, firstValue);
-    float second = ValueToFloat(pThis, secondValue);
-    return first == second;
-  }
-  if (firstValue->IsBoolean())
-    return firstValue->ToBoolean() == secondValue->ToBoolean();
-
-  return firstValue->IsNull() && secondValue && secondValue->IsNull();
-}
-
-// static
-std::vector<std::unique_ptr<CFXJSE_Value>> CFXJSE_FormCalcContext::unfoldArgs(
-    CFXJSE_Value* pThis,
-    CFXJSE_Arguments& args) {
-  std::vector<std::unique_ptr<CFXJSE_Value>> results;
-
-  int32_t iCount = 0;
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  int32_t argc = args.GetLength();
-  std::vector<std::unique_ptr<CFXJSE_Value>> argsValue;
-  static constexpr int kStart = 1;
-  for (int32_t i = 0; i < argc - kStart; i++) {
-    argsValue.push_back(args.GetValue(i + kStart));
-    if (argsValue[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      iCount += ((iLength > 2) ? (iLength - 2) : 0);
-    } else {
-      ++iCount;
-    }
-  }
-
-  for (int32_t i = 0; i < iCount; i++)
-    results.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  int32_t index = 0;
-  for (int32_t i = 0; i < argc - kStart; i++) {
-    if (argsValue[i]->IsArray()) {
-      auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectProperty("length", lengthValue.get());
-      int32_t iLength = lengthValue->ToInteger();
-      if (iLength < 3)
-        continue;
-
-      auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      argsValue[i]->GetObjectPropertyByIdx(1, propertyValue.get());
-      if (propertyValue->IsNull()) {
-        for (int32_t j = 2; j < iLength; j++) {
-          argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          GetObjectDefaultValue(jsObjectValue.get(), results[index].get());
-          index++;
-        }
-      } else {
-        for (int32_t j = 2; j < iLength; j++) {
-          argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
-          jsObjectValue->GetObjectProperty(
-              propertyValue->ToString().AsStringView(), results[index].get());
-          index++;
-        }
-      }
-    } else if (argsValue[i]->IsObject()) {
-      GetObjectDefaultValue(argsValue[i].get(), results[index].get());
-      index++;
-    } else {
-      results[index]->Assign(argsValue[i].get());
-      index++;
-    }
-  }
-  return results;
-}
-
-// static
-void CFXJSE_FormCalcContext::GetObjectDefaultValue(
-    CFXJSE_Value* pValue,
-    CFXJSE_Value* pDefaultValue) {
-  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue));
-  if (!pNode) {
-    pDefaultValue->SetNull();
-    return;
-  }
-  pNode->JSObject()->ScriptSomDefaultValue(pDefaultValue, false,
-                                           XFA_Attribute::Unknown);
-}
-
-// static
-bool CFXJSE_FormCalcContext::SetObjectDefaultValue(CFXJSE_Value* pValue,
-                                                   CFXJSE_Value* hNewValue) {
-  CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue));
-  if (!pNode)
-    return false;
-
-  pNode->JSObject()->ScriptSomDefaultValue(hNewValue, true,
-                                           XFA_Attribute::Unknown);
-  return true;
+  info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, returnValues));
 }
 
 // static
@@ -5400,6 +5258,8 @@
   if (bIsStar)
     return ByteString(bsName, "[*]");
 
+  // `iIndexFlags` values are the same as enum class
+  // `CXFA_FMIndexExpression::AccessorIndex` values.
   if (iIndexFlags == 0)
     return ByteString(bsName);
 
@@ -5410,311 +5270,53 @@
 
   const bool bNegative = iIndexValue < 0;
   ByteString bsSomExp(bsName);
-  if (iIndexFlags == 2)
+  if (iIndexFlags == 2) {
     bsSomExp += bNegative ? "[-" : "[+";
-  else
+  } else {
+    DCHECK_EQ(iIndexFlags, 3);
     bsSomExp += bNegative ? "[" : "[-";
-  iIndexValue = bNegative ? 0 - iIndexValue : iIndexValue;
-  bsSomExp += ByteString::FormatInteger(iIndexValue);
+  }
+
+  FX_SAFE_INT32 safe_index = iIndexValue;
+  if (bNegative)
+    safe_index = -safe_index;
+  bsSomExp += ByteString::FormatInteger(safe_index.ValueOrDefault(0));
   bsSomExp += "]";
   return bsSomExp;
 }
 
-// static
-bool CFXJSE_FormCalcContext::GetObjectForName(CFXJSE_Value* pThis,
-                                              CFXJSE_Value* accessorValue,
-                                              ByteStringView bsAccessorName) {
-  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
-  if (!pDoc)
-    return false;
+absl::optional<WideTextBuffer> CFXJSE_FormCalcContext::Translate(
+    cppgc::Heap* pHeap,
+    WideStringView wsFormcalc) {
+  if (wsFormcalc.IsEmpty())
+    return WideTextBuffer();
 
-  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  uint32_t dwFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
-                     XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
-  bool bRet = pScriptContext->ResolveObjects(
-      pScriptContext->GetThisObject(),
-      WideString::FromUTF8(bsAccessorName).AsStringView(), &resolveNodeRS,
-      dwFlags, nullptr);
-  if (bRet && resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    accessorValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(
-        resolveNodeRS.objects.front().Get()));
-    return true;
-  }
-  return false;
-}
-
-// static
-bool CFXJSE_FormCalcContext::ResolveObjects(CFXJSE_Value* pThis,
-                                            CFXJSE_Value* pRefValue,
-                                            ByteStringView bsSomExp,
-                                            XFA_RESOLVENODE_RS* resolveNodeRS,
-                                            bool bDotAccessor,
-                                            bool bHasNoResolveName) {
-  CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument();
-  if (!pDoc)
-    return false;
-
-  WideString wsSomExpression = WideString::FromUTF8(bsSomExp);
-  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
-  CXFA_Object* pNode = nullptr;
-  uint32_t dFlags = 0UL;
-  if (bDotAccessor) {
-    if (pRefValue && pRefValue->IsNull()) {
-      pNode = pScriptContext->GetThisObject();
-      dFlags = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
-    } else {
-      pNode = CFXJSE_Engine::ToObject(pRefValue);
-      if (!pNode)
-        return false;
-
-      if (bHasNoResolveName) {
-        WideString wsName;
-        if (CXFA_Node* pXFANode = pNode->AsNode()) {
-          Optional<WideString> ret =
-              pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false);
-          if (ret)
-            wsName = *ret;
-        }
-        if (wsName.IsEmpty())
-          wsName = L"#" + WideString::FromASCII(pNode->GetClassName());
-
-        wsSomExpression = wsName + wsSomExpression;
-        dFlags = XFA_RESOLVENODE_Siblings;
-      } else {
-        dFlags = (bsSomExp == "*")
-                     ? (XFA_RESOLVENODE_Children)
-                     : (XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-                        XFA_RESOLVENODE_Properties);
-      }
-    }
-  } else {
-    pNode = CFXJSE_Engine::ToObject(pRefValue);
-    dFlags = XFA_RESOLVENODE_AnyChild;
-  }
-  return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(),
-                                        resolveNodeRS, dFlags, nullptr);
-}
-
-// static
-void CFXJSE_FormCalcContext::ParseResolveResult(
-    CFXJSE_Value* pThis,
-    const XFA_RESOLVENODE_RS& resolveNodeRS,
-    CFXJSE_Value* pParentValue,
-    std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
-    bool* bAttribute) {
-  ASSERT(bAttribute);
-
-  resultValues->clear();
-
-  CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-
-  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    *bAttribute = false;
-    CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext();
-    for (auto& pObject : resolveNodeRS.objects) {
-      resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-      resultValues->back()->Assign(
-          pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get()));
-    }
-    return;
-  }
-
-  *bAttribute = true;
-  if (resolveNodeRS.script_attribute.callback &&
-      resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
-    for (auto& pObject : resolveNodeRS.objects) {
-      auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-      CJX_Object* jsObject = pObject->JSObject();
-      (*resolveNodeRS.script_attribute.callback)(
-          jsObject, pValue.get(), false,
-          resolveNodeRS.script_attribute.attribute);
-      resultValues->push_back(std::move(pValue));
-      *bAttribute = false;
-    }
-  }
-  if (!*bAttribute)
-    return;
-  if (!pParentValue || !pParentValue->IsObject())
-    return;
-
-  resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-  resultValues->back()->Assign(pParentValue);
-}
-
-// static
-int32_t CFXJSE_FormCalcContext::ValueToInteger(CFXJSE_Value* pThis,
-                                               CFXJSE_Value* pValue) {
-  if (!pValue || pValue->IsEmpty())
-    return 0;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  if (pValue->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    pValue->GetObjectPropertyByIdx(1, propertyValue.get());
-    pValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToInteger(pThis, newPropertyValue.get());
-    }
-
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToInteger(pThis, newPropertyValue.get());
-  }
-  if (pValue->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(pValue, newPropertyValue.get());
-    return ValueToInteger(pThis, newPropertyValue.get());
-  }
-  if (pValue->IsString())
-    return FXSYS_atoi(pValue->ToString().c_str());
-  return pValue->ToInteger();
-}
-
-// static
-float CFXJSE_FormCalcContext::ValueToFloat(CFXJSE_Value* pThis,
-                                           CFXJSE_Value* arg) {
-  if (!arg)
-    return 0.0f;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToFloat(pThis, newPropertyValue.get());
-    }
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToFloat(pThis, newPropertyValue.get());
-  }
-  if (arg->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(arg, newPropertyValue.get());
-    return ValueToFloat(pThis, newPropertyValue.get());
-  }
-  if (arg->IsString())
-    return strtof(arg->ToString().c_str(), nullptr);
-  if (arg->IsUndefined() || arg->IsEmpty())
-    return 0.0f;
-  return arg->ToFloat();
-}
-
-// static
-double CFXJSE_FormCalcContext::ValueToDouble(CFXJSE_Value* pThis,
-                                             CFXJSE_Value* arg) {
-  if (!arg)
-    return 0;
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  if (arg->IsArray()) {
-    auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    arg->GetObjectPropertyByIdx(1, propertyValue.get());
-    arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
-    if (propertyValue->IsNull()) {
-      GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
-      return ValueToDouble(pThis, newPropertyValue.get());
-    }
-    jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                     newPropertyValue.get());
-    return ValueToDouble(pThis, newPropertyValue.get());
-  }
-  if (arg->IsObject()) {
-    auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    GetObjectDefaultValue(arg, newPropertyValue.get());
-    return ValueToDouble(pThis, newPropertyValue.get());
-  }
-  if (arg->IsString())
-    return strtod(arg->ToString().c_str(), nullptr);
-  if (arg->IsUndefined() || arg->IsEmpty())
-    return 0;
-  return arg->ToDouble();
-}
-
-// static.
-double CFXJSE_FormCalcContext::ExtractDouble(CFXJSE_Value* pThis,
-                                             CFXJSE_Value* src,
-                                             bool* ret) {
-  ASSERT(ret);
-  *ret = true;
-
-  if (!src)
-    return 0;
-
-  if (!src->IsArray())
-    return ValueToDouble(pThis, src);
-
-  v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime();
-  auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  src->GetObjectProperty("length", lengthValue.get());
-  int32_t iLength = lengthValue->ToInteger();
-  if (iLength <= 2) {
-    *ret = false;
-    return 0.0;
-  }
-
-  auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  src->GetObjectPropertyByIdx(1, propertyValue.get());
-  src->GetObjectPropertyByIdx(2, jsObjectValue.get());
-  if (propertyValue->IsNull())
-    return ValueToDouble(pThis, jsObjectValue.get());
-
-  auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-  jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
-                                   newPropertyValue.get());
-  return ValueToDouble(pThis, newPropertyValue.get());
-}
-
-// static
-ByteString CFXJSE_FormCalcContext::ValueToUTF8String(CFXJSE_Value* arg) {
-  if (!arg || arg->IsNull() || arg->IsUndefined() || arg->IsEmpty())
-    return ByteString();
-  if (arg->IsBoolean())
-    return arg->ToBoolean() ? "1" : "0";
-  return arg->ToString();
-}
-
-// static.
-bool CFXJSE_FormCalcContext::Translate(WideStringView wsFormcalc,
-                                       CFX_WideTextBuf* wsJavascript) {
-  if (wsFormcalc.IsEmpty()) {
-    wsJavascript->Clear();
-    return true;
-  }
-
-  CXFA_FMParser parser(wsFormcalc);
-  std::unique_ptr<CXFA_FMAST> ast = parser.Parse();
+  CXFA_FMLexer lexer(wsFormcalc);
+  CXFA_FMParser parser(pHeap, &lexer);
+  CXFA_FMAST* ast = parser.Parse();
   if (!ast || parser.HasError())
-    return false;
+    return absl::nullopt;
 
   CXFA_FMToJavaScriptDepth::Reset();
-  if (!ast->ToJavaScript(wsJavascript))
-    return false;
+  absl::optional<WideTextBuffer> wsJavaScript = ast->ToJavaScript();
+  if (!wsJavaScript.has_value())
+    return absl::nullopt;
 
-  wsJavascript->AppendChar(0);
-  return !CXFA_IsTooBig(wsJavascript);
+  if (CXFA_IsTooBig(wsJavaScript.value()))
+    return absl::nullopt;
+
+  return wsJavaScript;
 }
 
-CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
+CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pIsolate,
                                                CFXJSE_Context* pScriptContext,
                                                CXFA_Document* pDoc)
-    : m_pIsolate(pScriptIsolate),
-      m_pValue(pdfium::MakeUnique<CFXJSE_Value>(pScriptIsolate)),
-      m_pDocument(pDoc) {
-  m_pValue->SetHostObject(
-      this,
-      CFXJSE_Class::Create(pScriptContext, &kFormCalcFM2JSDescriptor, false));
+    : m_pIsolate(pIsolate), m_pDocument(pDoc) {
+  m_Value.Reset(m_pIsolate,
+                NewBoundV8Object(
+                    m_pIsolate, CFXJSE_Class::Create(
+                                    pScriptContext, &kFormCalcDescriptor, false)
+                                    ->GetTemplate(m_pIsolate)));
 }
 
 CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() = default;
@@ -5723,18 +5325,18 @@
   return this;
 }
 
-void CFXJSE_FormCalcContext::GlobalPropertyGetter(CFXJSE_Value* pValue) {
-  pValue->Assign(m_pValue.get());
+v8::Local<v8::Value> CFXJSE_FormCalcContext::GlobalPropertyGetter() {
+  return v8::Local<v8::Value>::New(m_pIsolate, m_Value);
 }
 
 // static
-void CFXJSE_FormCalcContext::DotAccessorCommon(CFXJSE_Value* pThis,
-                                               ByteStringView bsFuncName,
-                                               CFXJSE_Arguments& args,
-                                               bool bDotAccessor) {
+void CFXJSE_FormCalcContext::DotAccessorCommon(
+    CFXJSE_HostObject* pThis,
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    bool bDotAccessor) {
   CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis);
-  v8::Isolate* pIsolate = pContext->GetScriptRuntime();
-  int32_t argc = args.GetLength();
+  v8::Isolate* pIsolate = pContext->GetIsolate();
+  int32_t argc = info.Length();
   if (argc < 4 || argc > 5) {
     pContext->ThrowCompilerErrorException();
     return;
@@ -5744,148 +5346,207 @@
   int32_t iIndexValue = 0;
   if (argc > 4) {
     bIsStar = false;
-    iIndexValue = ValueToInteger(pThis, args.GetValue(4).get());
+    iIndexValue = ValueToInteger(info.GetIsolate(), info[4]);
   }
 
-  const ByteString bsName = args.GetUTF8String(2);
+  const ByteString bsName =
+      fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[2]);
   const bool bHasNoResolveName = bDotAccessor && bsName.IsEmpty();
   ByteString bsSomExp = GenerateSomExpression(
-      bsName.AsStringView(), args.GetInt32(3), iIndexValue, bIsStar);
+      bsName.AsStringView(),
+      fxv8::ReentrantToInt32Helper(info.GetIsolate(), info[3]), iIndexValue,
+      bIsStar);
 
-  std::unique_ptr<CFXJSE_Value> argAccessor = args.GetValue(0);
-  if (argAccessor->IsArray()) {
-    auto pLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    argAccessor->GetObjectProperty("length", pLengthValue.get());
-    int32_t iLength = pLengthValue->ToInteger();
+  v8::Local<v8::Value> argAccessor = info[0];
+  if (fxv8::IsArray(argAccessor)) {
+    v8::Local<v8::Array> arr = argAccessor.As<v8::Array>();
+    uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
     if (iLength < 3) {
       pContext->ThrowArgumentMismatchException();
       return;
     }
 
-    int32_t iCounter = 0;
-    auto hJSObjValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
-    std::vector<std::vector<std::unique_ptr<CFXJSE_Value>>> resolveValues(
-        iLength - 2);
+    std::vector<std::vector<v8::Local<v8::Value>>> resolveValues(iLength - 2);
     bool bAttribute = false;
-    for (int32_t i = 2; i < iLength; i++) {
-      argAccessor->GetObjectPropertyByIdx(i, hJSObjValue.get());
-      XFA_RESOLVENODE_RS resolveNodeRS;
-      if (ResolveObjects(pThis, hJSObjValue.get(), bsSomExp.AsStringView(),
-                         &resolveNodeRS, bDotAccessor, bHasNoResolveName)) {
-        ParseResolveResult(pThis, resolveNodeRS, hJSObjValue.get(),
-                           &resolveValues[i - 2], &bAttribute);
-        iCounter += resolveValues[i - 2].size();
+    bool bAllEmpty = true;
+    for (uint32_t i = 2; i < iLength; i++) {
+      v8::Local<v8::Value> hJSObjValue =
+          fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, i);
+      absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+          ResolveObjects(pThis, hJSObjValue, bsSomExp.AsStringView(),
+                         bDotAccessor, bHasNoResolveName);
+      if (maybeResult.has_value()) {
+        resolveValues[i - 2] = ParseResolveResult(pThis, maybeResult.value(),
+                                                  hJSObjValue, &bAttribute);
+        bAllEmpty = bAllEmpty && resolveValues[i - 2].empty();
       }
     }
-    if (iCounter < 1) {
-      pContext->ThrowPropertyNotInObjectException(
-          WideString::FromUTF8(bsName.AsStringView()),
-          WideString::FromUTF8(bsSomExp.AsStringView()));
+    if (bAllEmpty) {
+      pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(),
+                                                  bsSomExp.AsStringView());
       return;
     }
 
-    std::vector<std::unique_ptr<CFXJSE_Value>> values;
-    for (int32_t i = 0; i < iCounter + 2; i++)
-      values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-    values[0]->SetInteger(1);
-    if (bAttribute)
-      values[1]->SetString(bsName.AsStringView());
-    else
-      values[1]->SetNull();
-
-    int32_t iIndex = 2;
-    for (int32_t i = 0; i < iLength - 2; i++) {
-      for (size_t j = 0; j < resolveValues[i].size(); j++) {
-        values[iIndex]->Assign(resolveValues[i][j].get());
-        iIndex++;
-      }
+    std::vector<v8::Local<v8::Value>> values;
+    values.push_back(fxv8::NewNumberHelper(pIsolate, 1));
+    values.push_back(
+        bAttribute ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView())
+                         .As<v8::Value>()
+                   : fxv8::NewNullHelper(pIsolate).As<v8::Value>());
+    for (uint32_t i = 0; i < iLength - 2; i++) {
+      for (size_t j = 0; j < resolveValues[i].size(); j++)
+        values.push_back(resolveValues[i][j]);
     }
-    args.GetReturnValue()->SetArray(values);
+    info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values));
     return;
   }
 
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  bool bRet = false;
-  ByteString bsAccessorName = args.GetUTF8String(1);
-  if (argAccessor->IsObject() ||
-      (argAccessor->IsNull() && bsAccessorName.IsEmpty())) {
-    bRet = ResolveObjects(pThis, argAccessor.get(), bsSomExp.AsStringView(),
-                          &resolveNodeRS, bDotAccessor, bHasNoResolveName);
-  } else if (!argAccessor->IsObject() && !bsAccessorName.IsEmpty() &&
-             GetObjectForName(pThis, argAccessor.get(),
-                              bsAccessorName.AsStringView())) {
-    bRet = ResolveObjects(pThis, argAccessor.get(), bsSomExp.AsStringView(),
-                          &resolveNodeRS, bDotAccessor, bHasNoResolveName);
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult;
+  ByteString bsAccessorName =
+      fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[1]);
+  if (fxv8::IsObject(argAccessor) ||
+      (fxv8::IsNull(argAccessor) && bsAccessorName.IsEmpty())) {
+    maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(),
+                                 bDotAccessor, bHasNoResolveName);
+  } else if (!fxv8::IsObject(argAccessor) && !bsAccessorName.IsEmpty()) {
+    v8::Local<v8::Value> obj =
+        GetObjectForName(pThis, bsAccessorName.AsStringView());
+    if (!obj.IsEmpty()) {
+      argAccessor = obj;
+      maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(),
+                                   bDotAccessor, bHasNoResolveName);
+    }
   }
-  if (!bRet) {
-    pContext->ThrowPropertyNotInObjectException(
-        WideString::FromUTF8(bsName.AsStringView()),
-        WideString::FromUTF8(bsSomExp.AsStringView()));
+  if (!maybeResult.has_value()) {
+    pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(),
+                                                bsSomExp.AsStringView());
     return;
   }
 
-  std::vector<std::unique_ptr<CFXJSE_Value>> resolveValues;
   bool bAttribute = false;
-  ParseResolveResult(pThis, resolveNodeRS, argAccessor.get(), &resolveValues,
-                     &bAttribute);
+  std::vector<v8::Local<v8::Value>> resolveValues =
+      ParseResolveResult(pThis, maybeResult.value(), argAccessor, &bAttribute);
 
-  std::vector<std::unique_ptr<CFXJSE_Value>> values;
-  for (size_t i = 0; i < resolveValues.size() + 2; i++)
-    values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
-
-  values[0]->SetInteger(1);
-  if (bAttribute)
-    values[1]->SetString(bsName.AsStringView());
-  else
-    values[1]->SetNull();
+  std::vector<v8::Local<v8::Value>> values(resolveValues.size() + 2);
+  values[0] = fxv8::NewNumberHelper(pIsolate, 1);
+  values[1] = bAttribute
+                  ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView())
+                        .As<v8::Value>()
+                  : fxv8::NewNullHelper(pIsolate).As<v8::Value>();
 
   for (size_t i = 0; i < resolveValues.size(); i++)
-    values[i + 2]->Assign(resolveValues[i].get());
+    values[i + 2] = resolveValues[i];
 
-  args.GetReturnValue()->SetArray(values);
+  info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values));
+}
+
+bool CFXJSE_FormCalcContext::ApplyToExpansion(
+    std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    bool bStrict) {
+  v8::Isolate* pIsolate = info.GetIsolate();
+  for (int32_t i = 0; i < info.Length(); i++) {
+    v8::Local<v8::Value> argValue = info[i];
+    if (fxv8::IsArray(argValue)) {
+      if (!ApplyToArray(pIsolate, fn, argValue.As<v8::Array>()) && bStrict) {
+        ThrowArgumentMismatchException();
+        return false;
+      }
+    } else if (fxv8::IsObject(argValue)) {
+      ApplyToObject(pIsolate, fn, argValue.As<v8::Object>());
+    } else if (!fxv8::IsNull(argValue)) {
+      fn(pIsolate, argValue);
+    }
+  }
+  return true;
+}
+
+bool CFXJSE_FormCalcContext::ApplyToArray(
+    v8::Isolate* pIsolate,
+    std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+    v8::Local<v8::Array> pArray) {
+  uint32_t iLength = fxv8::GetArrayLengthHelper(pArray);
+  if (iLength < 3)
+    return false;
+
+  v8::Local<v8::Value> propertyValue =
+      fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, 1);
+
+  ByteString bsName;
+  const bool nullprop = fxv8::IsNull(propertyValue);
+  if (!nullprop)
+    bsName = fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue);
+
+  for (uint32_t j = 2; j < iLength; j++) {
+    v8::Local<v8::Value> jsValue =
+        fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, j);
+    if (!fxv8::IsObject(jsValue))
+      continue;
+
+    v8::Local<v8::Object> jsObjectValue = jsValue.As<v8::Object>();
+    v8::Local<v8::Value> newPropertyValue =
+        nullprop ? GetObjectDefaultValue(pIsolate, jsObjectValue)
+                 : fxv8::ReentrantGetObjectPropertyHelper(
+                       pIsolate, jsObjectValue, bsName.AsStringView());
+    if (!fxv8::IsNull(newPropertyValue))
+      fn(pIsolate, newPropertyValue);
+  }
+  return true;
+}
+
+void CFXJSE_FormCalcContext::ApplyToObject(
+    v8::Isolate* pIsolate,
+    std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+    v8::Local<v8::Object> pObject) {
+  v8::Local<v8::Value> newPropertyValue =
+      GetObjectDefaultValue(pIsolate, pObject);
+  if (!fxv8::IsNull(newPropertyValue))
+    fn(pIsolate, newPropertyValue);
 }
 
 void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException(
     ByteStringView name) const {
-  ThrowException(WideString::FromUTF8(name) +
-                 WideString::FromASCII(" doesn't have a default property."));
+  ByteString msg(name);
+  msg += " doesn't have a default property.";
+  ThrowException(msg.AsStringView());
 }
 
 void CFXJSE_FormCalcContext::ThrowCompilerErrorException() const {
-  ThrowException(WideString::FromASCII("Compiler error."));
+  ThrowException("Compiler error.");
 }
 
 void CFXJSE_FormCalcContext::ThrowDivideByZeroException() const {
-  ThrowException(WideString::FromASCII("Divide by zero."));
+  ThrowException("Divide by zero.");
 }
 
 void CFXJSE_FormCalcContext::ThrowServerDeniedException() const {
-  ThrowException(WideString::FromASCII("Server does not permit operation."));
+  ThrowException("Server does not permit operation.");
 }
 
 void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException(
-    const WideString& name,
-    const WideString& exp) const {
-  ThrowException(
-      WideString::FromASCII("An attempt was made to reference property '") +
-      name + WideString::FromASCII("' of a non-object in SOM expression ") +
-      exp + L".");
+    ByteStringView name,
+    ByteStringView exp) const {
+  ByteString msg("An attempt was made to reference property '");
+  msg += name;
+  msg += "' of a non-object in SOM expression ";
+  msg += exp;
+  msg += ".";
+  ThrowException(msg.AsStringView());
 }
 
 void CFXJSE_FormCalcContext::ThrowParamCountMismatchException(
-    const WideString& method) const {
-  ThrowException(
-      WideString::FromASCII("Incorrect number of parameters calling method '") +
-      method + L"'.");
+    ByteStringView method) const {
+  ByteString msg("Incorrect number of parameters calling method '");
+  msg += method;
+  msg += "'.";
+  ThrowException(msg.AsStringView());
 }
 
 void CFXJSE_FormCalcContext::ThrowArgumentMismatchException() const {
-  ThrowException(WideString::FromASCII(
-      "Argument mismatch in property or function argument."));
+  ThrowException("Argument mismatch in property or function argument.");
 }
 
-void CFXJSE_FormCalcContext::ThrowException(const WideString& str) const {
-  ASSERT(!str.IsEmpty());
-  FXJSE_ThrowMessage(str.ToUTF8().AsStringView());
+void CFXJSE_FormCalcContext::ThrowException(ByteStringView str) const {
+  DCHECK(!str.IsEmpty());
+  FXJSE_ThrowMessage(GetIsolate(), str);
 }
diff --git a/fxjs/xfa/cfxjse_formcalc_context.h b/fxjs/xfa/cfxjse_formcalc_context.h
index cf1bc95..3c7d271 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.h
+++ b/fxjs/xfa/cfxjse_formcalc_context.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,21 +7,27 @@
 #ifndef FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
 #define FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
 
-#include <memory>
-#include <vector>
+#include <stdint.h>
 
-#include "core/fxcrt/unowned_ptr.h"
+#include <functional>
+
+#include "core/fxcrt/widetext_buffer.h"
 #include "fxjs/xfa/fxjse.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "v8/include/cppgc/persistent.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
-class CFXJSE_Arguments;
 class CFXJSE_Context;
-class CFX_WideTextBuf;
 class CXFA_Document;
 
+namespace cppgc {
+class Heap;
+}  // namespace cppgc
+
 class CFXJSE_FormCalcContext final : public CFXJSE_HostObject {
  public:
-  CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
+  CFXJSE_FormCalcContext(v8::Isolate* pIsolate,
                          CFXJSE_Context* pScriptContext,
                          CXFA_Document* pDoc);
   ~CFXJSE_FormCalcContext() override;
@@ -29,387 +35,279 @@
   // CFXJSE_HostObject:
   CFXJSE_FormCalcContext* AsFormCalcContext() override;
 
-  static void Abs(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Avg(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Ceil(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Count(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Floor(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Max(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Min(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Mod(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Round(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Sum(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Date(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Date2Num(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void DateFmt(CFXJSE_Value* pThis,
-                      ByteStringView bsFuncName,
-                      CFXJSE_Arguments& args);
-  static void IsoDate2Num(CFXJSE_Value* pThis,
-                          ByteStringView bsFuncName,
-                          CFXJSE_Arguments& args);
-  static void IsoTime2Num(CFXJSE_Value* pThis,
-                          ByteStringView bsFuncName,
-                          CFXJSE_Arguments& args);
-  static void LocalDateFmt(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void LocalTimeFmt(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void Num2Date(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void Num2GMTime(CFXJSE_Value* pThis,
-                         ByteStringView bsFuncName,
-                         CFXJSE_Arguments& args);
-  static void Num2Time(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void Time(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Time2Num(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void TimeFmt(CFXJSE_Value* pThis,
-                      ByteStringView bsFuncName,
-                      CFXJSE_Arguments& args);
+  static void Abs(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Avg(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Ceil(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Count(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Floor(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Max(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Min(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Mod(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Round(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Sum(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Date(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Date2Num(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void DateFmt(CFXJSE_HostObject* pThis,
+                      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void IsoDate2Num(CFXJSE_HostObject* pThis,
+                          const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void IsoTime2Num(CFXJSE_HostObject* pThis,
+                          const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void LocalDateFmt(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void LocalTimeFmt(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Num2Date(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Num2GMTime(CFXJSE_HostObject* pThis,
+                         const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Num2Time(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Time(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Time2Num(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void TimeFmt(CFXJSE_HostObject* pThis,
+                      const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static ByteString Local2IsoDate(CFXJSE_Value* pThis,
+  static ByteString Local2IsoDate(CFXJSE_HostObject* pThis,
                                   ByteStringView bsDate,
                                   ByteStringView bsFormat,
                                   ByteStringView bsLocale);
-  static ByteString IsoDate2Local(CFXJSE_Value* pThis,
+  static ByteString IsoDate2Local(CFXJSE_HostObject* pThis,
                                   ByteStringView bsDate,
                                   ByteStringView bsFormat,
                                   ByteStringView bsLocale);
-  static ByteString IsoTime2Local(CFXJSE_Value* pThis,
+  static ByteString IsoTime2Local(CFXJSE_HostObject* pThis,
                                   ByteStringView bsTime,
                                   ByteStringView bsFormat,
                                   ByteStringView bsLocale);
-  static ByteString GetLocalDateFormat(CFXJSE_Value* pThis,
+  static ByteString GetLocalDateFormat(CFXJSE_HostObject* pThis,
                                        int32_t iStyle,
                                        ByteStringView bsLocale,
                                        bool bStandard);
-  static ByteString GetLocalTimeFormat(CFXJSE_Value* pThis,
+  static ByteString GetLocalTimeFormat(CFXJSE_HostObject* pThis,
                                        int32_t iStyle,
                                        ByteStringView bsLocale,
                                        bool bStandard);
-  static ByteString GetStandardDateFormat(CFXJSE_Value* pThis,
+  static ByteString GetStandardDateFormat(CFXJSE_HostObject* pThis,
                                           int32_t iStyle,
                                           ByteStringView bsLocale);
-  static ByteString GetStandardTimeFormat(CFXJSE_Value* pThis,
+  static ByteString GetStandardTimeFormat(CFXJSE_HostObject* pThis,
                                           int32_t iStyle,
                                           ByteStringView bsLocale);
-  static ByteString Num2AllTime(CFXJSE_Value* pThis,
+  static ByteString Num2AllTime(CFXJSE_HostObject* pThis,
                                 int32_t iTime,
                                 ByteStringView bsFormat,
                                 ByteStringView bsLocale,
                                 bool bGM);
 
-  static void Apr(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void CTerm(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void FV(CFXJSE_Value* pThis,
-                 ByteStringView bsFuncName,
-                 CFXJSE_Arguments& args);
-  static void IPmt(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void NPV(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Pmt(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void PPmt(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void PV(CFXJSE_Value* pThis,
-                 ByteStringView bsFuncName,
-                 CFXJSE_Arguments& args);
-  static void Rate(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Term(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Choose(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Exists(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void HasValue(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void Oneof(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Within(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void If(CFXJSE_Value* pThis,
-                 ByteStringView bsFuncName,
-                 CFXJSE_Arguments& args);
-  static void Eval(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Ref(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void UnitType(CFXJSE_Value* pThis,
-                       ByteStringView bsFuncName,
-                       CFXJSE_Arguments& args);
-  static void UnitValue(CFXJSE_Value* pThis,
-                        ByteStringView bsFuncName,
-                        CFXJSE_Arguments& args);
+  static void Apr(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void CTerm(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void FV(CFXJSE_HostObject* pThis,
+                 const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void IPmt(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void NPV(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Pmt(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void PPmt(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void PV(CFXJSE_HostObject* pThis,
+                 const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Rate(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Term(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Choose(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Exists(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void HasValue(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Oneof(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Within(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void If(CFXJSE_HostObject* pThis,
+                 const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Eval(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Ref(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void UnitType(CFXJSE_HostObject* pThis,
+                       const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void UnitValue(CFXJSE_HostObject* pThis,
+                        const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static void At(CFXJSE_Value* pThis,
-                 ByteStringView bsFuncName,
-                 CFXJSE_Arguments& args);
-  static void Concat(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Decode(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Encode(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Format(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Left(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Len(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Lower(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Ltrim(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Parse(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Replace(CFXJSE_Value* pThis,
-                      ByteStringView bsFuncName,
-                      CFXJSE_Arguments& args);
-  static void Right(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Rtrim(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Space(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Str(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Stuff(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void Substr(CFXJSE_Value* pThis,
-                     ByteStringView bsFuncName,
-                     CFXJSE_Arguments& args);
-  static void Uuid(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Upper(CFXJSE_Value* pThis,
-                    ByteStringView bsFuncName,
-                    CFXJSE_Arguments& args);
-  static void WordNum(CFXJSE_Value* pThis,
-                      ByteStringView bsFuncName,
-                      CFXJSE_Arguments& args);
+  static void At(CFXJSE_HostObject* pThis,
+                 const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Concat(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Decode(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Encode(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Format(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Left(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Len(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Lower(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Ltrim(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Parse(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Replace(CFXJSE_HostObject* pThis,
+                      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Right(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Rtrim(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Space(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Str(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Stuff(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Substr(CFXJSE_HostObject* pThis,
+                     const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Uuid(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Upper(CFXJSE_HostObject* pThis,
+                    const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void WordNum(CFXJSE_HostObject* pThis,
+                      const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static void Get(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void Post(CFXJSE_Value* pThis,
-                   ByteStringView bsFuncName,
-                   CFXJSE_Arguments& args);
-  static void Put(CFXJSE_Value* pThis,
-                  ByteStringView bsFuncName,
-                  CFXJSE_Arguments& args);
-  static void assign_value_operator(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args);
-  static void logical_or_operator(CFXJSE_Value* pThis,
-                                  ByteStringView bsFuncName,
-                                  CFXJSE_Arguments& args);
-  static void logical_and_operator(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args);
-  static void equality_operator(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args);
-  static void notequality_operator(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args);
-  static bool fm_ref_equal(CFXJSE_Value* pThis, CFXJSE_Arguments& args);
-  static void less_operator(CFXJSE_Value* pThis,
-                            ByteStringView bsFuncName,
-                            CFXJSE_Arguments& args);
-  static void lessequal_operator(CFXJSE_Value* pThis,
-                                 ByteStringView bsFuncName,
-                                 CFXJSE_Arguments& args);
-  static void greater_operator(CFXJSE_Value* pThis,
-                               ByteStringView bsFuncName,
-                               CFXJSE_Arguments& args);
-  static void greaterequal_operator(CFXJSE_Value* pThis,
-                                    ByteStringView bsFuncName,
-                                    CFXJSE_Arguments& args);
-  static void plus_operator(CFXJSE_Value* pThis,
-                            ByteStringView bsFuncName,
-                            CFXJSE_Arguments& args);
-  static void minus_operator(CFXJSE_Value* pThis,
-                             ByteStringView bsFuncName,
-                             CFXJSE_Arguments& args);
-  static void multiple_operator(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args);
-  static void divide_operator(CFXJSE_Value* pThis,
-                              ByteStringView bsFuncName,
-                              CFXJSE_Arguments& args);
-  static void positive_operator(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args);
-  static void negative_operator(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args);
-  static void logical_not_operator(CFXJSE_Value* pThis,
-                                   ByteStringView bsFuncName,
-                                   CFXJSE_Arguments& args);
-  static void dot_accessor(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void dotdot_accessor(CFXJSE_Value* pThis,
-                              ByteStringView bsFuncName,
-                              CFXJSE_Arguments& args);
-  static void eval_translation(CFXJSE_Value* pThis,
-                               ByteStringView bsFuncName,
-                               CFXJSE_Arguments& args);
-  static void is_fm_object(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void is_fm_array(CFXJSE_Value* pThis,
-                          ByteStringView bsFuncName,
-                          CFXJSE_Arguments& args);
-  static void get_fm_value(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void get_fm_jsobj(CFXJSE_Value* pThis,
-                           ByteStringView bsFuncName,
-                           CFXJSE_Arguments& args);
-  static void fm_var_filter(CFXJSE_Value* pThis,
-                            ByteStringView bsFuncName,
-                            CFXJSE_Arguments& args);
-  static void concat_fm_object(CFXJSE_Value* pThis,
-                               ByteStringView bsFuncName,
-                               CFXJSE_Arguments& args);
+  static void Get(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Post(CFXJSE_HostObject* pThis,
+                   const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void Put(CFXJSE_HostObject* pThis,
+                  const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void assign_value_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void logical_or_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void logical_and_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void equality_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void notequality_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static bool fm_ref_equal(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void less_operator(CFXJSE_HostObject* pThis,
+                            const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void lessequal_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void greater_operator(CFXJSE_HostObject* pThis,
+                               const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void greaterequal_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void plus_operator(CFXJSE_HostObject* pThis,
+                            const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void minus_operator(CFXJSE_HostObject* pThis,
+                             const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void multiple_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void divide_operator(CFXJSE_HostObject* pThis,
+                              const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void positive_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void negative_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void logical_not_operator(
+      CFXJSE_HostObject* pThis,
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void dot_accessor(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void dotdot_accessor(CFXJSE_HostObject* pThis,
+                              const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void eval_translation(CFXJSE_HostObject* pThis,
+                               const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void is_fm_object(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void is_fm_array(CFXJSE_HostObject* pThis,
+                          const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void get_fm_value(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void get_fm_jsobj(CFXJSE_HostObject* pThis,
+                           const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void fm_var_filter(CFXJSE_HostObject* pThis,
+                            const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void concat_fm_object(CFXJSE_HostObject* pThis,
+                               const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static int32_t hvalue_get_array_length(CFXJSE_Value* pThis,
-                                         CFXJSE_Value* arg);
-  static bool simpleValueCompare(CFXJSE_Value* pThis,
-                                 CFXJSE_Value* firstValue,
-                                 CFXJSE_Value* secondValue);
-  static std::vector<std::unique_ptr<CFXJSE_Value>> unfoldArgs(
-      CFXJSE_Value* pThis,
-      CFXJSE_Arguments& args);
-  static void GetObjectDefaultValue(CFXJSE_Value* pObjectValue,
-                                    CFXJSE_Value* pDefaultValue);
-  static bool SetObjectDefaultValue(CFXJSE_Value* pObjectValue,
-                                    CFXJSE_Value* pNewValue);
   static ByteString GenerateSomExpression(ByteStringView bsName,
                                           int32_t iIndexFlags,
                                           int32_t iIndexValue,
                                           bool bIsStar);
-  static bool GetObjectForName(CFXJSE_Value* pThis,
-                               CFXJSE_Value* accessorValue,
-                               ByteStringView bsAccessorName);
-  static bool ResolveObjects(CFXJSE_Value* pThis,
-                             CFXJSE_Value* pParentValue,
-                             ByteStringView bsSomExp,
-                             XFA_RESOLVENODE_RS* resolveNodeRS,
-                             bool bdotAccessor,
-                             bool bHasNoResolveName);
-  static void ParseResolveResult(
-      CFXJSE_Value* pThis,
-      const XFA_RESOLVENODE_RS& resolveNodeRS,
-      CFXJSE_Value* pParentValue,
-      std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
-      bool* bAttribute);
+  static absl::optional<WideTextBuffer> Translate(cppgc::Heap* pHeap,
+                                                  WideStringView wsFormcalc);
 
-  static std::unique_ptr<CFXJSE_Value> GetSimpleValue(CFXJSE_Value* pThis,
-                                                      CFXJSE_Arguments& args,
-                                                      uint32_t index);
-  static bool ValueIsNull(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static int32_t ValueToInteger(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static float ValueToFloat(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static double ValueToDouble(CFXJSE_Value* pThis, CFXJSE_Value* pValue);
-  static ByteString ValueToUTF8String(CFXJSE_Value* pValue);
-  static double ExtractDouble(CFXJSE_Value* pThis,
-                              CFXJSE_Value* src,
-                              bool* ret);
-
-  static bool Translate(WideStringView wsFormcalc,
-                        CFX_WideTextBuf* wsJavascript);
-
-  void GlobalPropertyGetter(CFXJSE_Value* pValue);
-
- private:
-  static void DotAccessorCommon(CFXJSE_Value* pThis,
-                                ByteStringView bsFuncName,
-                                CFXJSE_Arguments& args,
-                                bool bDotAccessor);
-
-  v8::Isolate* GetScriptRuntime() const { return m_pIsolate.Get(); }
+  v8::Local<v8::Value> GlobalPropertyGetter();
+  v8::Isolate* GetIsolate() const { return m_pIsolate; }
   CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
 
+ private:
+  static void DotAccessorCommon(CFXJSE_HostObject* pThis,
+                                const v8::FunctionCallbackInfo<v8::Value>& info,
+                                bool bDotAccessor);
+
+  bool ApplyToExpansion(
+      std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+      const v8::FunctionCallbackInfo<v8::Value>& info,
+      bool bStrict);
+
+  bool ApplyToArray(v8::Isolate* pIsolate,
+                    std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+                    v8::Local<v8::Array> pArray);
+
+  void ApplyToObject(v8::Isolate* pIsolate,
+                     std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
+                     v8::Local<v8::Object> pObject);
+
+  void ThrowArgumentMismatchException() const;
   void ThrowNoDefaultPropertyException(ByteStringView name) const;
   void ThrowCompilerErrorException() const;
   void ThrowDivideByZeroException() const;
   void ThrowServerDeniedException() const;
-  void ThrowPropertyNotInObjectException(const WideString& name,
-                                         const WideString& exp) const;
-  void ThrowArgumentMismatchException() const;
-  void ThrowParamCountMismatchException(const WideString& method) const;
-  void ThrowException(const WideString& str) const;
+  void ThrowPropertyNotInObjectException(ByteStringView name,
+                                         ByteStringView exp) const;
+  void ThrowParamCountMismatchException(ByteStringView method) const;
+  void ThrowException(ByteStringView str) const;
 
-  UnownedPtr<v8::Isolate> m_pIsolate;
-  std::unique_ptr<CFXJSE_Value> m_pValue;
-  UnownedPtr<CXFA_Document> const m_pDocument;
+  UnownedPtr<v8::Isolate> const m_pIsolate;
+  v8::Global<v8::Value> m_Value;
+  cppgc::WeakPersistent<CXFA_Document> const m_pDocument;
 };
 
 #endif  // FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_
diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
index 19fc730..1478bf9 100644
--- a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
@@ -1,11 +1,17 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <math.h>
+
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/scoped_set_tz.h"
 #include "testing/xfa_js_embedder_test.h"
+#include "third_party/base/cxx17_backports.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 
 class CFXJSE_FormCalcContextEmbedderTest : public XFAJSEmbedderTest {
@@ -14,8 +20,85 @@
   ~CFXJSE_FormCalcContextEmbedderTest() override = default;
 
  protected:
-  bool ExecuteExpectNull(ByteStringView input) {
-    return Execute(input) && GetValue()->IsNull();
+  CFXJSE_Context* GetJseContext() {
+    return GetScriptContext()->GetJseContextForTest();
+  }
+
+  void ExecuteExpectError(ByteStringView input) {
+    EXPECT_FALSE(Execute(input)) << "Program: " << input;
+  }
+
+  void ExecuteExpectNull(ByteStringView input) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    EXPECT_TRUE(fxv8::IsNull(GetValue())) << "Program: " << input;
+  }
+
+  void ExecuteExpectBool(ByteStringView input, bool expected) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+
+    // Yes, bools might be integers, somehow.
+    EXPECT_TRUE(fxv8::IsBoolean(value) || fxv8::IsInteger(value))
+        << "Program: " << input;
+    EXPECT_EQ(expected, fxv8::ReentrantToBooleanHelper(isolate(), value))
+        << "Program: " << input;
+  }
+
+  void ExecuteExpectInt32(ByteStringView input, int32_t expected) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsInteger(value)) << "Program: " << input;
+    EXPECT_EQ(expected, fxv8::ReentrantToInt32Helper(isolate(), value))
+        << "Program: " << input;
+  }
+
+  void ExecuteExpectFloat(ByteStringView input, float expected) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_FLOAT_EQ(expected, fxv8::ReentrantToFloatHelper(isolate(), value))
+        << "Program: " << input;
+  }
+
+  void ExecuteExpectFloatNear(ByteStringView input, float expected) {
+    constexpr float kPrecision = 0.000001f;
+
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_NEAR(expected, fxv8::ReentrantToFloatHelper(isolate(), value),
+                kPrecision)
+        << "Program: " << input;
+  }
+
+  void ExecuteExpectNaN(ByteStringView input) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_TRUE(isnan(fxv8::ReentrantToDoubleHelper(isolate(), value)));
+  }
+
+  void ExecuteExpectString(ByteStringView input, const char* expected) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsString(value));
+    EXPECT_STREQ(expected,
+                 fxv8::ReentrantToByteStringHelper(isolate(), value).c_str())
+        << "Program: " << input;
   }
 };
 
@@ -32,309 +115,167 @@
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, TranslateNumber) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  const char input[] = "123";
-  EXPECT_TRUE(Execute(input));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_EQ(123, value->ToInteger()) << "Program: " << input;
+  ExecuteExpectInt32("123", 123);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Numeric) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"123 + 456", 579},
-               {"2 - 3 * 10 / 2 + 7", -6},
-               {"10 * 3 + 5 * 4", 50},
-               {"(5 - \"abc\") * 3", 15},
-               {"\"100\" / 10e1", 1},
-               {"5 + null + 3", 8},
-               // {"if (\"abc\") then\n"
-               //  "  10\n"
-               //  "else\n"
-               //  "  20\n"
-               //  "endif",
-               //  20},
-               // {"3 / 0 + 1", 0},
-               {"-(17)", -17},
-               {"-(-17)", 17},
-               {"+(17)", 17},
-               {"+(-17)", -17},
-               {"if (1 < 2) then\n1\nendif", 1},
-               {"if (\"abc\" > \"def\") then\n"
-                "  1 and 0\n"
-                "else\n"
-                "  0\n"
-                "endif",
-                0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("123 + 456", 579);
+  ExecuteExpectInt32("2 - 3 * 10 / 2 + 7", -6);
+  ExecuteExpectInt32("10 * 3 + 5 * 4", 50);
+  ExecuteExpectInt32("(5 - \"abc\") * 3", 15);
+  ExecuteExpectInt32("\"100\" / 10e1", 1);
+  ExecuteExpectInt32("5 + null + 3", 8);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectInt32(
+      "if (\"abc\") then\n"
+      "  10\n"
+      "else\n"
+      "  20\n"
+      "endif",
+      20);
+  ExecuteExpectInt32("3 / 0 + 1", 0);
+#endif
+  ExecuteExpectInt32("-(17)", -17);
+  ExecuteExpectInt32("-(-17)", 17);
+  ExecuteExpectInt32("+(17)", 17);
+  ExecuteExpectInt32("+(-17)", -17);
+  ExecuteExpectInt32("if (1 < 2) then\n1\nendif", 1);
+  ExecuteExpectInt32(
+      "if (\"abc\" > \"def\") then\n"
+      "  1 and 0\n"
+      "else\n"
+      "  0\n"
+      "endif",
+      0);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Strings) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"\"abc\"", "abc"},
-      {"concat(\"The total is \", 2, \" dollars and \", 57, \" cents.\")",
-       "The total is 2 dollars and 57 cents."}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("\"abc\"", "abc");
+  ExecuteExpectString(
+      "concat(\"The total is \", 2, \" dollars and \", 57, \" cents.\")",
+      "The total is 2 dollars and 57 cents.");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Booleans) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"0 and 1 or 2 > 1", true},
-               {"2 < 3 not 1 == 1", false},
-               {"\"abc\" | 2", true},
-               {"1 or 0", true},
-               {"0 | 0", false},
-               {"0 or 1 | 0 or 0", true},
-               {"1 and 0", false},
-               // {"0 & 0", true},  // TODO(dsinclair) Confirm with Reader.
-               {"0 and 1 & 0 and 0", false},
-               {"not(\"true\")", true},
-               {"not(1)", false},
-               {"3 == 3", true},
-               {"3 <> 4", true},
-               {"\"abc\" eq \"def\"", false},
-               {"\"def\" ne \"abc\"", true},
-               {"5 + 5 == 10", true},
-               {"5 + 5 <> \"10\"", false},
-               {"3 < 3", false},
-               {"3 > 4", false},
-               {"\"abc\" <= \"def\"", true},
-               {"\"def\" > \"abc\"", true},
-               {"12 >= 12", true},
-               {"\"true\" < \"false\"", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectBool("0 and 1 or 2 > 1", true);
+  ExecuteExpectBool("2 < 3 not 1 == 1", false);
+  ExecuteExpectBool("\"abc\" | 2", true);
+  ExecuteExpectBool("1 or 0", true);
+  ExecuteExpectBool("0 | 0", false);
+  ExecuteExpectBool("0 or 1 | 0 or 0", true);
+  ExecuteExpectBool("1 and 0", false);
+  ExecuteExpectBool("0 and 1 & 0 and 0", false);
+  ExecuteExpectBool("not(\"true\")", true);
+  ExecuteExpectBool("not(1)", false);
+  ExecuteExpectBool("3 == 3", true);
+  ExecuteExpectBool("3 <> 4", true);
+  ExecuteExpectBool("\"abc\" eq \"def\"", false);
+  ExecuteExpectBool("\"def\" ne \"abc\"", true);
+  ExecuteExpectBool("5 + 5 == 10", true);
+  ExecuteExpectBool("5 + 5 <> \"10\"", false);
+  ExecuteExpectBool("3 < 3", false);
+  ExecuteExpectBool("3 > 4", false);
+  ExecuteExpectBool("\"abc\" <= \"def\"", true);
+  ExecuteExpectBool("\"def\" > \"abc\"", true);
+  ExecuteExpectBool("12 >= 12", true);
+  ExecuteExpectBool("\"true\" < \"false\"", false);
+#if 0
+  // TODO(thestig): Investigate this case.
+  // Confirm with Reader.
+  ExecuteExpectBool("0 & 0", true);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Abs) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Abs(1.03)", 1.03f}, {"Abs(-1.03)", 1.03f}, {"Abs(0)", 0.0f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("Abs(1.03)", 1.03f);
+  ExecuteExpectFloat("Abs(-1.03)", 1.03f);
+  ExecuteExpectFloat("Abs(0)", 0.0f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Avg) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Avg(0, 32, 16)", 16.0f}, {"Avg(2.5, 17, null)", 9.75f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("Avg(0, 32, 16)", 16.0f);
+  ExecuteExpectFloat("Avg(2.5, 17, null)", 9.75f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ceil) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Ceil(2.5875)", 3}, {"Ceil(-5.9)", -5}, {"Ceil(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Ceil(2.5875)", 3);
+  ExecuteExpectInt32("Ceil(-5.9)", -5);
+  ExecuteExpectInt32("Ceil(\"abc\")", 0);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Count) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Count(\"Tony\", \"Blue\", 41)", 3}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Count(\"Tony\", \"Blue\", 41)", 3);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Floor) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Floor(21.3409873)", 21},
-               {"Floor(5.999965342)", 5},
-               {"Floor(3.2 * 15)", 48}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Floor(21.3409873)", 21);
+  ExecuteExpectInt32("Floor(5.999965342)", 5);
+  ExecuteExpectInt32("Floor(3.2 * 15)", 48);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Max) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Max(234, 15, 107)", 234},
-               {"Max(\"abc\", 15, \"Tony Blue\")", 15},
-               {"Max(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Max(234, 15, 107)", 234);
+  ExecuteExpectInt32("Max(\"abc\", 15, \"Tony Blue\")", 15);
+  ExecuteExpectInt32("Max(\"abc\")", 0);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Min) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Min(234, 15, 107)", 15},
-               // TODO(dsinclair): Verify with Reader; I believe this should
-               // have a return of 0.
-               // {"Min(\"abc\", 15, \"Tony Blue\")", 15},
-               {"Min(\"abc\")", 0}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Min(234, 15, 107)", 15);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  // Verify with Reader; This should have a return value of 0.
+  ExecuteExpectInt32("Min(\"abc\", 15, \"Tony Blue\")", 15);
+#endif
+  ExecuteExpectInt32("Min(\"abc\")", 0);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Mod) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Mod(64, -3)", 1}, {"Mod(-13, 3)", -1}, {"Mod(\"abc\", 2)", 0}};
+  ExecuteExpectInt32("Mod(64, -3)", 1);
+  ExecuteExpectInt32("Mod(-13, 3)", -1);
+  ExecuteExpectInt32("Mod(\"abc\", 2)", 0);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectNaN("Mod(10, NaN)");
+  ExecuteExpectNaN("Mod(10, Infinity)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Round) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Round(12.389764537, 4)", 12.3898f},
-               {"Round(20/3, 2)", 6.67f},
-               {"Round(8.9897, \"abc\")", 9.0f},
-               {"Round(FV(400, 0.10/12, 30*12), 2)", 904195.17f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program;
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("Round(12.389764537, 4)", 12.3898f);
+  ExecuteExpectFloat("Round(20/3, 2)", 6.67f);
+  ExecuteExpectFloat("Round(8.9897, \"abc\")", 9.0f);
+  ExecuteExpectFloat("Round(FV(400, 0.10/12, 30*12), 2)", 904195.17f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Sum) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"Sum(2, 4, 6, 8)", 20},
-               {"Sum(-2, 4, -6, 8)", 4},
-               {"Sum(4, 16, \"abc\", 19)", 39}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Sum(2, 4, 6, 8)", 20);
+  ExecuteExpectInt32("Sum(-2, 4, -6, 8)", 4);
+  ExecuteExpectInt32("Sum(4, 16, \"abc\", 19)", 39);
 }
 
 // TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Date) {
@@ -346,7 +287,7 @@
 
 //   EXPECT_TRUE(Execute("Date()"));
 
-//   CFXJSE_Value* value = GetValue();
+//   v8::Local<v8::Value> value = GetValue();
 //   EXPECT_TRUE(value->IsNumber());
 //   EXPECT_EQ(days, value->ToInteger());
 // }
@@ -354,203 +295,100 @@
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Date2Num) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      // {"Date2Num(\"Mar 15, 1996\")", 35138},
-      {"Date2Num(\"1/1/1900\", \"D/M/YYYY\")", 1},
-      {"Date2Num(\"03/15/96\", \"MM/DD/YY\")", 35138},
-      // {"Date2Num(\"Aug 1, 1996\", \"MMM D, YYYY\")", 35277},
-      {"Date2Num(\"96-08-20\", \"YY-MM-DD\", \"fr_FR\")", 35296},
-      {"Date2Num(\"1/3/00\", \"D/M/YY\") - Date2Num(\"1/2/00\", \"D/M/YY\")",
-       29}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Date2Num(\"1/1/1900\", \"D/M/YYYY\")", 1);
+  ExecuteExpectInt32("Date2Num(\"03/15/96\", \"MM/DD/YY\")", 35138);
+  ExecuteExpectInt32("Date2Num(\"96-08-20\", \"YY-MM-DD\", \"fr_FR\")", 35296);
+  ExecuteExpectInt32(
+      "Date2Num(\"1/3/00\", \"D/M/YY\") - Date2Num(\"1/2/00\", \"D/M/YY\")",
+      29);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectInt32("Date2Num(\"Mar 15, 1996\")", 35138);
+  ExecuteExpectInt32("Date2Num(\"Aug 1, 1996\", \"MMM D, YYYY\")", 35277);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DateFmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"DateFmt(1)", "M/D/YY"},
-      // {"DateFmt(2, \"fr_CA\")", "YY-MM-DD"},
-      {"DateFmt(3, \"de_DE\")", "D. MMMM YYYY"},
-      // {"DateFmt(4, \"fr_FR\")", "EEE D' MMMM YYYY"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("DateFmt(3, \"de_DE\")", "D. MMMM YYYY");
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectString("DateFmt(1)", "M/D/YY");
+  ExecuteExpectString("DateFmt(2, \"fr_CA\")", "YY-MM-DD");
+  ExecuteExpectString("DateFmt(4, \"fr_FR\")", "EEE D' MMMM YYYY");
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, IsoDate2Num) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"IsoDate2Num(\"1900\")", 1},
-               {"IsoDate2Num(\"1900-01\")", 1},
-               {"IsoDate2Num(\"1900-01-01\")", 1},
-               {"IsoDate2Num(\"19960315T20:20:20\")", 35138},
-               {"IsoDate2Num(\"2000-03-01\") - IsoDate2Num(\"20000201\")", 29}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("IsoDate2Num(\"1900\")", 1);
+  ExecuteExpectInt32("IsoDate2Num(\"1900-01\")", 1);
+  ExecuteExpectInt32("IsoDate2Num(\"1900-01-01\")", 1);
+  ExecuteExpectInt32("IsoDate2Num(\"19960315T20:20:20\")", 35138);
+  ExecuteExpectInt32("IsoDate2Num(\"2000-03-01\") - IsoDate2Num(\"20000201\")",
+                     29);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_IsoTime2Num) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"IsoTime2Num(\"00:00:00Z\")", 1}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("IsoTime2Num(\"00:00:00Z\")", 1);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, LocalDateFmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {// {"LocalDateFmt(1, \"de_DE\")", "tt.MM.uu"},
-               // {"LocalDateFmt(2, \"fr_CA\")", "aa-MM-jj"},
-               {"LocalDateFmt(3, \"de_CH\")", "t. MMMM jjjj"},
-               {"LocalDateFmt(4, \"fr_FR\")", "EEEE j MMMM aaaa"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("LocalDateFmt(3, \"de_CH\")", "t. MMMM jjjj");
+  ExecuteExpectString("LocalDateFmt(4, \"fr_FR\")", "EEEE j MMMM aaaa");
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectString("LocalDateFmt(1, \"de_DE\")", "tt.MM.uu");
+  ExecuteExpectString("LocalDateFmt(2, \"fr_CA\")", "aa-MM-jj");
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_LocalTimeFmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"LocalTimeFmt(1, \"de_DE\")", "HH:mm"},
-               {"LocalTimeFmt(2, \"fr_CA\")", "HH:mm::ss"},
-               {"LocalTimeFmt(3, \"de_CH\")", "HH:mm:ss z"},
-               {"LocalTimeFmt(4, \"fr_FR\")", "HH' h 'mm z"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("LocalTimeFmt(1, \"de_DE\")", "HH:mm");
+  ExecuteExpectString("LocalTimeFmt(2, \"fr_CA\")", "HH:mm::ss");
+  ExecuteExpectString("LocalTimeFmt(3, \"de_CH\")", "HH:mm:ss z");
+  ExecuteExpectString("LocalTimeFmt(4, \"fr_FR\")", "HH' h 'mm z");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Num2Date) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"Num2Date(1, \"DD/MM/YYYY\")", "01/01/1900"},
-      {"Num2Date(35139, \"DD-MMM-YYYY\", \"de_DE\")", "16-Mrz-1996"},
-      // {"Num2Date(Date2Num(\"Mar 15, 2000\") - Date2Num(\"98-03-15\", "
-      //  "\"YY-MM-DD\", \"fr_CA\"))",
-      //  "Jan 1, 1902"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString()) << "Program: " << tests[i].program;
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Num2Date(1, \"DD/MM/YYYY\")", "01/01/1900");
+  ExecuteExpectString("Num2Date(35139, \"DD-MMM-YYYY\", \"de_DE\")",
+                      "16-Mrz-1996");
+#if 0
+  // TODO(thestig): Investigate this case.
+  ExecuteExpectString(
+      "Num2Date(Date2Num(\"Mar 15, 2000\") - Date2Num(\"98-03-15\", "
+      "\"YY-MM-DD\", \"fr_CA\"))",
+      "Jan 1, 1902");
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2GMTime) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {// Broken on Windows only.
-               {"Num2GMTime(1, \"HH:MM:SS\")", "00:00:00"},
-               // Below broken on other platforms.
-               {"Num2GMTime(65593001, \"HH:MM:SS Z\")", "18:13:13 GMT"},
-               {"Num2GMTime(43993001, TimeFmt(4, \"de_DE\"), \"de_DE\")",
-                "12.13 Uhr GMT"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Broken on Windows only.
+  ExecuteExpectString("Num2GMTime(1, \"HH:MM:SS\")", "00:00:00");
+  // Below broken on other platforms.
+  ExecuteExpectString("Num2GMTime(65593001, \"HH:MM:SS Z\")", "18:13:13 GMT");
+  ExecuteExpectString("Num2GMTime(43993001, TimeFmt(4, \"de_DE\"), \"de_DE\")",
+                      "12.13 Uhr GMT");
 }
 
 // TODO(dsinclair): Broken on Mac ...
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2Time) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Num2Time(1, \"HH:MM:SS\")", "00:00:00"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Num2Time(1, \"HH:MM:SS\")", "00:00:00");
 }
 
 // TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Time) {
@@ -561,7 +399,7 @@
 
 //   EXPECT_TRUE(Execute("Time()"));
 
-//   CFXJSE_Value* value = GetValue();
+//   v8::Local<v8::Value> value = GetValue();
 //   EXPECT_TRUE(value->IsInteger());
 //   EXPECT_EQ(tp.tv_sec * 1000L + tp.tv_usec / 1000, value->ToInteger())
 //       << "Program: Time()";
@@ -570,653 +408,352 @@
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Time2Num) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      // {"Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1},
-      {"Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\", \"fr_FR\")", 47593001}};
+  ExecuteExpectInt32("Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1);
+  ExecuteExpectInt32("Time2Num(\"00:00:01 GMT\", \"HH:MM:SS Z\")", 1001);
+  ExecuteExpectInt32("Time2Num(\"00:01:00 GMT\", \"HH:MM:SS Z\")", 60001);
+  ExecuteExpectInt32("Time2Num(\"01:00:00 GMT\", \"HH:MM:SS Z\")", 3600001);
+  ExecuteExpectInt32("Time2Num(\"23:59:59 GMT\", \"HH:MM:SS Z\")", 86399001);
+  // https://crbug.com/pdfium/1257
+  ExecuteExpectInt32("Time2Num(\"\", \"\", 1)", 0);
+  ExecuteExpectInt32("Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\", \"fr_FR\")",
+                     47593001);
+}
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Time2NumWithTZ) {
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
+  static constexpr const char* kTimeZones[] = {
+      "UTC+14",   "UTC-14",   "UTC+9:30", "UTC-0:30",
+      "UTC+0:30", "UTC-0:01", "UTC+0:01"};
+  for (const char* tz : kTimeZones) {
+    ScopedSetTZ scoped_set_tz(tz);
+    ExecuteExpectInt32("Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1);
+    ExecuteExpectInt32("Time2Num(\"11:59:59 GMT\", \"HH:MM:SS Z\")", 43199001);
+    ExecuteExpectInt32("Time2Num(\"12:00:00 GMT\", \"HH:MM:SS Z\")", 43200001);
+    ExecuteExpectInt32("Time2Num(\"23:59:59 GMT\", \"HH:MM:SS Z\")", 86399001);
+  }
+  {
+    ScopedSetTZ scoped_set_tz("UTC-3:00");
+    ExecuteExpectInt32("Time2Num(\"1:13:13 PM\")", 36793001);
+    ExecuteExpectInt32(
+        "Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\") - "
+        "Time2Num(\"13:13:13\", \"HH:MM:SS\")",
+        10800000);
   }
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, TimeFmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"TimeFmt(1)", "h::MM A"},
-      {"TimeFmt(2, \"fr_CA\")", "HH:MM:SS"},
-      {"TimeFmt(3, \"fr_FR\")", "HH:MM:SS Z"},
-      // {"TimeFmt(4, \"de_DE\")", "H.MM' Uhr 'Z"}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("TimeFmt(2, \"fr_CA\")", "HH:MM:SS");
+  ExecuteExpectString("TimeFmt(3, \"fr_FR\")", "HH:MM:SS Z");
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectString("TimeFmt(1)", "h::MM A");
+  ExecuteExpectString("TimeFmt(4, \"de_DE\")", "H.MM' Uhr 'Z");
+#endif
 }
 
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Apr) {
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Apr) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Apr(35000, 269.50, 360)", 0.08515404566f},
-               {"Apr(210000 * 0.75, 850 + 110, 25 * 26)", 0.07161332404f}};
+  ExecuteExpectFloatNear("Apr(35000, 269.50, 360)", 0.08515404566f);
+  ExecuteExpectFloatNear("Apr(210000 * 0.75, 850 + 110, 25 * 26)",
+                         0.07161332404f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectError("Apr(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, CTerm) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      // {"CTerm(0.02, 1000, 100)", 116.2767474515f},
-      {"CTerm(0.10, 500000, 12000)", 39.13224648502f},
-      // {"CTerm(0.0275 + 0.0025, 1000000, 55000 * 0.10)", 176.02226044975f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("CTerm(0.10, 500000, 12000)", 39.13224648502f);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  ExecuteExpectFloat("CTerm(0.02, 1000, 100)", 116.2767474515f);
+  ExecuteExpectFloat("CTerm(0.0275 + 0.0025, 1000000, 55000 * 0.10)",
+                     176.02226044975f);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, FV) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f},
-               {"FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}};
+  ExecuteExpectFloat("FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f);
+  ExecuteExpectFloat("FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectError("FV(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, IPmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"IPmt(30000, 0.085, 295.50, 7, 3)", 624.8839283142f},
-               {"IPmt(160000, 0.0475, 980, 24, 12)", 7103.80833569485f},
-               {"IPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("IPmt(30000, 0.085, 295.50, 7, 3)", 624.8839283142f);
+  ExecuteExpectFloat("IPmt(160000, 0.0475, 980, 24, 12)", 7103.80833569485f);
+  ExecuteExpectFloat("IPmt(15000, 0.065, 65.50, 15, 1)", 0.0f);
 }
 
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_NPV) {
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, NPV) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"NPV(0.065, 5000)", 4694.83568075117f},
-               {"NPV(0.10, 500, 1500, 4000, 10000)", 11529.60863329007f},
-               {"NPV(0.0275 / 12, 50, 60, 40, 100, 25)", 273.14193838457f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program;
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("NPV(0.065, 5000)", 4694.83568075117f);
+  ExecuteExpectFloat("NPV(0.10, 500, 1500, 4000, 10000)", 11529.60863329007f);
+  ExecuteExpectFloat("NPV(0.0275 / 12, 50, 60, 40, 100, 25)", 273.14193838457f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Pmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {// {"Pmt(150000, 0.0475 / 12, 25 * 12)", 855.17604207164f},
-               {"Pmt(25000, 0.085, 12)", 3403.82145169876f}};
+  ExecuteExpectFloat("Pmt(25000, 0.085, 12)", 3403.82145169876f);
+  ExecuteExpectFloat("Pmt(5000, 0.01, 1)", 5050);
+  ExecuteExpectFloat("Pmt(5000, 0.01, 1.5)", 5050);
+  ExecuteExpectFloat("Pmt(30000.00, .085 / 12, 12 * 12)", 333.01666929435f);
+  ExecuteExpectFloat("Pmt(10000, .08 / 12, 10)", 1037.03208935916f);
+  ExecuteExpectFloat("Pmt(150000, 0.0475 / 12, 25 * 12)", 855.17604207164f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  // https://crbug.com/1293179
+  ExecuteExpectError("Pmt(2, 2, 99999997952)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, PPmt) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"PPmt(30000, 0.085, 295.50, 7, 3)", 261.6160716858f},
-      {"PPmt(160000, 0.0475, 980, 24, 12)", 4656.19166430515f},
-      // {"PPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("PPmt(30000, 0.085, 295.50, 7, 3)", 261.6160716858f);
+  ExecuteExpectFloat("PPmt(160000, 0.0475, 980, 24, 12)", 4656.19166430515f);
+#if 0
+  // TODO(thestig): Investigate this case.
+  ExecuteExpectFloat("PPmt(15000, 0.065, 65.50, 15, 1)", 0.0f);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, PV) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"PV(400, 0.10 / 12, 30 * 12)", 45580.32799074439f},
-      // {"PV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}
-  };
+  ExecuteExpectFloat("PV(400, 0.10 / 12, 30 * 12)", 45580.32799074439f);
+  ExecuteExpectFloat("PV(1000, 0.075 / 4, 10 * 4)", 27964.88770467326f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  // https://crbug.com/1296840
+  ExecuteExpectError("PV(2, 2, 2147483648)");
 }
 
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Rate) {
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rate) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {{"Rate(12000, 8000, 5)", 0.0844717712f},
-               {"Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f}};
+  ExecuteExpectFloatNear("Rate(12000, 8000, 5)", 0.0844717712f);
+  ExecuteExpectFloatNear("Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f);
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectError("Rate(2, 2, 2147483648)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Term) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {// {"Term(475, .05, 1500)", 3.00477517728f},
-               {"Term(2500, 0.0275 + 0.0025, 5000)", 1.97128786369f}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("Term(2500, 0.0275 + 0.0025, 5000)", 1.97128786369f);
+#if 0
+  // TODO(thestig): Investigate this case.
+  ExecuteExpectFloat("Term(475, .05, 1500)", 3.00477517728f);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Choose) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      {"Choose(3, \"Taxes\", \"Price\", \"Person\", \"Teller\")", "Person"},
-      {"Choose(2, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", "9"},
-      {"Choose(20/3, \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\")",
-       "F"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Choose(3, \"Taxes\", \"Price\", \"Person\", \"Teller\")",
+                      "Person");
+  ExecuteExpectString("Choose(2, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", "9");
+  ExecuteExpectString(
+      "Choose(20/3, \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\")",
+      "F");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Exists) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  EXPECT_TRUE(Execute("Exists(\"hello world\")"));
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_FALSE(value->ToBoolean());
+  ExecuteExpectBool("Exists(\"hello world\")", false);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, HasValue) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"HasValue(2)", true}, {"HasValue(\" \")", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectBool("HasValue(2)", true);
+  ExecuteExpectBool("HasValue(\" \")", false);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Oneof) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {
-      {"Oneof(3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", true},
-      {"Oneof(\"John\", \"Bill\", \"Gary\", \"Joan\", \"John\", \"Lisa\")",
-       true},
-      {"Oneof(3, 1, 25)", false},
-      {"Oneof(3, 3, null)", true},
-      {"Oneof(3, null, null)", false},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectBool("Oneof(3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", true);
+  ExecuteExpectBool(
+      "Oneof(\"John\", \"Bill\", \"Gary\", \"Joan\", \"John\", \"Lisa\")",
+      true);
+  ExecuteExpectBool("Oneof(3, 1, 25)", false);
+  ExecuteExpectBool("Oneof(3, 3, null)", true);
+  ExecuteExpectBool("Oneof(3, null, null)", false);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Within) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    bool result;
-  } tests[] = {{"Within(\"C\", \"A\", \"D\")", true},
-               {"Within(1.5, 0, 2)", true},
-               {"Within(-1, 0, 2)", false}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program;
-    EXPECT_EQ(tests[i].result, value->ToBoolean())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectBool("Within(\"C\", \"A\", \"D\")", true);
+  ExecuteExpectBool("Within(1.5, 0, 2)", true);
+  ExecuteExpectBool("Within(-1, 0, 2)", false);
 }
 
-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Eval) {
+TEST_F(CFXJSE_FormCalcContextEmbedderTest, Eval) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"eval(\"10*3+5*4\")", 50}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("eval(\"10*3+5*4\")", 50);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Null) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Null()", "null"},
-               {"Concat(\"ABC\", Null(), \"DEF\")", "ABCDEF"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
-
-  EXPECT_TRUE(Execute("Null() + 5"));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsInteger());
-  EXPECT_EQ(5, value->ToInteger());
+  ExecuteExpectString("Null()", "null");
+  ExecuteExpectString("Concat(\"ABC\", Null(), \"DEF\")", "ABCDEF");
+  ExecuteExpectInt32("Null() + 5", 5);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ref) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Ref(\"10*3+5*4\")", "10*3+5*4"}, {"Ref(\"hello\")", "hello"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Ref(\"10*3+5*4\")", "10*3+5*4");
+  ExecuteExpectString("Ref(\"hello\")", "hello");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitType) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"UnitType(\"36 in\")", "in"},
-               {"UnitType(\"2.54centimeters\")", "cm"},
-               {"UnitType(\"picas\")", "pt"},
-               {"UnitType(\"2.cm\")", "cm"},
-               {"UnitType(\"2.zero cm\")", "in"},
-               {"UnitType(\"kilometers\")", "in"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("UnitType(\"36 in\")", "in");
+  ExecuteExpectString("UnitType(\"2.54centimeters\")", "cm");
+  ExecuteExpectString("UnitType(\"picas\")", "pt");
+  ExecuteExpectString("UnitType(\"2.cm\")", "cm");
+  ExecuteExpectString("UnitType(\"2.zero cm\")", "in");
+  ExecuteExpectString("UnitType(\"kilometers\")", "in");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitValue) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    float result;
-  } tests[] = {
-      {"UnitValue(\"2in\")", 2.0f}, {"UnitValue(\"2in\", \"cm\")", 5.08f},
-      // {"UnitValue(\"6\", \"pt\")", 432f},
-      // {"UnitType(\"A\", \"cm\")", 0.0f},
-      // {"UnitType(\"5.08cm\", \"kilograms\")", 2.0f}
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber());
-    EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectFloat("UnitValue(\"2in\")", 2.0f);
+  ExecuteExpectFloat("UnitValue(\"2in\", \"cm\")", 5.08f);
+#if 0
+  // TODO(thestig): Investigate these cases.
+  // Should the UnitType cases move into the UnitType test case?
+  ExecuteExpectFloat("UnitValue(\"6\", \"pt\")", 432f);
+  ExecuteExpectFloat("UnitType(\"A\", \"cm\")", 0.0f);
+  ExecuteExpectFloat("UnitType(\"5.08cm\", \"kilograms\")", 2.0f);
+#endif
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, At) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {{"At(\"ABCDEFGH\", \"AB\")", 1},
-               {"At(\"ABCDEFGH\", \"F\")", 6},
-               {"At(23412931298471, 29)", 5}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("At(\"ABCDEFGH\", \"AB\")", 1);
+  ExecuteExpectInt32("At(\"ABCDEFGH\", \"F\")", 6);
+  ExecuteExpectInt32("At(23412931298471, 29)", 5);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Concat) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Concat(\"ABC\", \"DEF\")", "ABCDEF"},
-               {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"},
-               {"Concat(\"You owe \", WordNum(1154.67, 2), \".\")",
-                "You owe One Thousand One Hundred Fifty-four Dollars And "
-                "Sixty-seven Cents."}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Concat(\"ABC\", \"DEF\")", "ABCDEF");
+  ExecuteExpectString("Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue");
+  ExecuteExpectString("Concat(\"You owe \", WordNum(1154.67, 2), \".\")",
+                      "You owe One Thousand One Hundred Fifty-four Dollars And "
+                      "Sixty-seven Cents.");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Decode) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // HTML
-      {R"(Decode("", "html"))", ""},
-      {R"(Decode("abc&Acirc;xyz", "html"))", "abc\xC3\x82xyz"},
-      {R"(Decode("abc&NoneSuchButVeryLongIndeed;", "html"))", "abc"},
-      {R"(Decode("&#x0041;&AElig;&Aacute;", "html"))", "A\xC3\x86\xC3\x81"},
-      {R"(Decode("xyz&#", "html"))", "xyz"},
-      {R"(Decode("|&zzzzzz;|", "html"))", "||"},
+  // HTML
+  ExecuteExpectString(R"(Decode("", "html"))", "");
+  ExecuteExpectString(R"(Decode("abc&Acirc;xyz", "html"))", "abc\xC3\x82xyz");
+  ExecuteExpectString(R"(Decode("abc&NoneSuchButVeryLongIndeed;", "html"))",
+                      "abc");
+  ExecuteExpectString(R"(Decode("&#x0041;&AElig;&Aacute;", "html"))",
+                      "A\xC3\x86\xC3\x81");
+  ExecuteExpectString(R"(Decode("xyz&#", "html"))", "xyz");
+  ExecuteExpectString(R"(Decode("|&zzzzzz;|", "html"))", "||");
 
-      // XML
-      {R"(Decode("", "xml"))", ""},
-      {R"(Decode("~!@#$%%^&amp;*()_+|`", "xml"))", "~!@#$%%^&*()_+|`"},
-      {R"(Decode("abc&nonesuchbutverylongindeed;", "xml"))", "abc"},
-      {R"(Decode("&quot;&#x45;&lt;&gt;[].&apos;", "xml"))", "\"E<>[].'"},
-      {R"(Decode("xyz&#", "xml"))", "xyz"},
-      {R"(Decode("|&zzzzzz;|", "xml"))", "||"},
+  // XML
+  ExecuteExpectString(R"(Decode("", "xml"))", "");
+  ExecuteExpectString(R"(Decode("~!@#$%%^&amp;*()_+|`", "xml"))",
+                      "~!@#$%%^&*()_+|`");
+  ExecuteExpectString(R"(Decode("abc&nonesuchbutverylongindeed;", "xml"))",
+                      "abc");
+  ExecuteExpectString(R"(Decode("&quot;&#x45;&lt;&gt;[].&apos;", "xml"))",
+                      "\"E<>[].'");
+  ExecuteExpectString(R"(Decode("xyz&#", "xml"))", "xyz");
+  ExecuteExpectString(R"(Decode("|&zzzzzz;|", "xml"))", "||");
 
-      // URL
-      {R"(Decode("", "url"))", ""},
-      {R"(Decode("~%26^&*()_+|`{", "url"))", "~&^&*()_+|`{"},
-      {R"(Decode("~%26^&*()_+|`{", "mbogo"))", "~&^&*()_+|`{"},
-      {R"(Decode("~%26^&*()_+|`{"))", "~&^&*()_+|`{"},
-      {R"(Decode("~%~~"))", ""},
-      {R"(Decode("?%~"))", ""},
-      {R"(Decode("?%"))", "?"},
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // URL
+  ExecuteExpectString(R"(Decode("", "url"))", "");
+  ExecuteExpectString(R"(Decode("~%26^&*()_+|`{", "url"))", "~&^&*()_+|`{");
+  ExecuteExpectString(R"(Decode("~%26^&*()_+|`{", "mbogo"))", "~&^&*()_+|`{");
+  ExecuteExpectString(R"(Decode("~%26^&*()_+|`{"))", "~&^&*()_+|`{");
+  ExecuteExpectString(R"(Decode("~%~~"))", "");
+  ExecuteExpectString(R"(Decode("?%~"))", "");
+  ExecuteExpectString(R"(Decode("?%"))", "?");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Encode) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-    {"Encode(\"X/~&^*<=>?|\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
-    {"Encode(\"X/~&^*<=>?|\", \"mbogo\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
-    {"Encode(\"X/~&^*<=>?|\", \"url\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"},
-    {"Encode(\"X/~&^*<=>?|\", \"xml\")", "X/~&amp;^*&lt;=&gt;?|"},
-    {"Encode(\"X/~&^*<=>?|\", \"html\")", "X/~&amp;^*&lt;=&gt;?|"},
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\")",
+                      "X%2f%7e%26%5e*%3c%3d%3e%3f%7c");
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"mbogo\")",
+                      "X%2f%7e%26%5e*%3c%3d%3e%3f%7c");
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"url\")",
+                      "X%2f%7e%26%5e*%3c%3d%3e%3f%7c");
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"xml\")",
+                      "X/~&amp;^*&lt;=&gt;?|");
+  ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"html\")",
+                      "X/~&amp;^*&lt;=&gt;?|");
 
-    {"Encode(\"\\u0022\\u00f5\\ufed0\", \"url\")", "%22%f5%fe%d0"},
-    {"Encode(\"\\u0022\\u00f4\\ufed0\", \"xml\")", "&quot;&#xf4;&#xfed0;"},
-    {"Encode(\"\\u0022\\u00f5\\ufed0\", \"html\")", "&quot;&otilde;&#xfed0;"},
+  ExecuteExpectString("Encode(\"\\u0022\\u00f5\\ufed0\", \"url\")",
+                      "%22%f5%fe%d0");
+  ExecuteExpectString("Encode(\"\\u0022\\u00f4\\ufed0\", \"xml\")",
+                      "&quot;&#xf4;&#xfed0;");
+  ExecuteExpectString("Encode(\"\\u0022\\u00f5\\ufed0\", \"html\")",
+                      "&quot;&otilde;&#xfed0;");
 
-#if !defined(OS_WIN)
-    // Windows wchar_t isn't wide enough to handle these anyways.
-    // TODO(tsepez): fix surrogate encodings.
-    {"Encode(\"\\uD83D\\uDCA9\", \"url\")", "%01%f4%a9"},
-    {"Encode(\"\\uD83D\\uDCA9\", \"xml\")", ""},
-    {"Encode(\"\\uD83D\\uDCA9\", \"html\")", ""},
-#endif  // !defined(OS_WIN)
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+#if !BUILDFLAG(IS_WIN)
+  // Windows wchar_t isn't wide enough to handle these anyways.
+  // TODO(tsepez): fix surrogate encodings.
+  ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"url\")", "%01%f4%a9");
+  ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"xml\")", "");
+  ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"html\")", "");
+#endif  // !BUILDFLAG(IS_WIN)
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Format) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Format(\"MMM D, YYYY\", \"20020901\")", "Sep 1, 2002"},
-               {"Format(\"$9,999,999.99\", 1234567.89)", "$1,234,567.89"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Format(\"MMM D, YYYY\", \"20020901\")", "Sep 1, 2002");
+  ExecuteExpectString("Format(\"$9,999,999.99\", 1234567.89)", "$1,234,567.89");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Left) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Left(\"ABCDEFGH\", 3)", "ABC"},
-               {"Left(\"Tony Blue\", 5)", "Tony "}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Left(\"ABCDEFGH\", 3)", "ABC");
+  ExecuteExpectString("Left(\"Tony Blue\", 5)", "Tony ");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Len) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    int result;
-  } tests[] = {
-      {"Len(\"ABCDEFGH\")", 8}, {"Len(4)", 1}, {"Len(Str(4.532, 6, 4))", 6}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger());
-    EXPECT_EQ(tests[i].result, value->ToInteger())
-        << "Program: " << tests[i].program;
-  }
+  ExecuteExpectInt32("Len(\"ABCDEFGH\")", 8);
+  ExecuteExpectInt32("Len(4)", 1);
+  ExecuteExpectInt32("Len(Str(4.532, 6, 4))", 6);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Lower) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Lower(\"ABC\")", "abc"},
-               {"Lower(\"21 Main St.\")", "21 main st."},
-               {"Lower(15)", "15"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Lower(\"ABC\")", "abc");
+  ExecuteExpectString("Lower(\"21 Main St.\")", "21 main st.");
+  ExecuteExpectString("Lower(15)", "15");
 }
 
 // This is testing for an OOB read, so will likely only fail under ASAN.
@@ -1232,311 +769,262 @@
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ltrim) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Ltrim(\"   ABCD\")", "ABCD"},
-               {"Ltrim(Rtrim(\"    Tony Blue    \"))", "Tony Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Ltrim(\"   ABCD\")", "ABCD");
+  ExecuteExpectString("Ltrim(Rtrim(\"    Tony Blue    \"))", "Tony Blue");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Parse) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Parse(\"MMM D, YYYY\", \"Sep 1, 2002\")", "2002-09-01"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
-
-  EXPECT_TRUE(Execute("Parse(\"$9,999,999.99\", \"$1,234,567.89\")"));
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsNumber());
-  EXPECT_FLOAT_EQ(1234567.89f, value->ToFloat());
+  ExecuteExpectString("Parse(\"MMM D, YYYY\", \"Sep 1, 2002\")", "2002-09-01");
+  ExecuteExpectFloat("Parse(\"$9,999,999.99\", \"$1,234,567.89\")",
+                     1234567.89f);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Replace) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Replace(\"Tony Blue\", \"Tony\", \"Chris\")", "Chris Blue"},
-               {"Replace(\"ABCDEFGH\", \"D\")", "ABCEFGH"},
-               {"Replace(\"ABCDEFGH\", \"d\")", "ABCDEFGH"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Replace(\"Tony Blue\", \"Tony\", \"Chris\")",
+                      "Chris Blue");
+  ExecuteExpectString("Replace(\"ABCDEFGH\", \"D\")", "ABCEFGH");
+  ExecuteExpectString("Replace(\"ABCDEFGH\", \"d\")", "ABCDEFGH");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Right) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Right(\"ABCDEFGH\", 3)", "FGH"},
-               {"Right(\"Tony Blue\", 5)", " Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Right(\"ABCDEFGH\", 3)", "FGH");
+  ExecuteExpectString("Right(\"Tony Blue\", 5)", " Blue");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rtrim) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Rtrim(\"ABCD   \")", "ABCD"},
-               {"Rtrim(\"Tony Blue      \t\")", "Tony Blue"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Rtrim(\"ABCD   \")", "ABCD");
+  ExecuteExpectString("Rtrim(\"Tony Blue      \t\")", "Tony Blue");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Space) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Space(5)", "     "},
-               {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"}};
+  ExecuteExpectString("Space(5)", "     ");
+  ExecuteExpectString("Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Error cases.
+  ExecuteExpectError("Space(15654909)");
+  ExecuteExpectError("Space(99999999)");
+  ExecuteExpectError("Space()");
+  ExecuteExpectError("Space(1, 2)");
+  ExecuteExpectNull("Space( $)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Str) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Str(2.456)", "         2"},
-               {"Str(4.532, 6, 4)", "4.5320"},
-               {"Str(234.458, 4)", " 234"},
-               {"Str(31.2345, 4, 2)", "****"}};
+  ExecuteExpectString("Str(2.456)", "         2");
+  ExecuteExpectString("Str(4.532, 6, 4)", "4.5320");
+  ExecuteExpectString("Str(234.458, 4)", " 234");
+  ExecuteExpectString("Str(31.2345, 4, 2)", "****");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
+  // Test maximum "n3" precision value.
+  ExecuteExpectString("Str(-765, 19, 14)", "-765.00000000000000");
+  ExecuteExpectString("Str(-765, 20, 15)", "-765.000000000000000");
+  ExecuteExpectString("Str(-765, 21, 16)", " -765.000000000000000");
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Error cases.
+  ExecuteExpectError("Str()");
+  ExecuteExpectError("Str(1, 2, 3, 4)");
+  ExecuteExpectError("Str(42, 15654909)");
+  ExecuteExpectNull("Str( $)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Stuff) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Stuff(\"TonyBlue\", 5, 0, \" \")", "Tony Blue"},
-               {"Stuff(\"ABCDEFGH\", 4, 2)", "ABCFGH"},
-               {"Stuff(\"members-list@myweb.com\", 0, 0, \"cc:\")",
-                "cc:members-list@myweb.com"}};
+  // Test wrong number of parameters.
+  ExecuteExpectError("Stuff(1, 2)");
+  ExecuteExpectError("Stuff(1, 2, 3, 4, 5)");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
+  // Test null arguments.
+  ExecuteExpectNull("Stuff(null, 0, 4)");
+  ExecuteExpectNull("Stuff(\"ABCDEFG\", null, 4)");
+  ExecuteExpectNull("Stuff(\"ABCDEFG\", 0, null)");
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Insertions.
+  ExecuteExpectString("Stuff(\"\", 0, 0, \"clams\")", "clams");
+  ExecuteExpectString("Stuff(\"TonyBlue\", 5, 0, \" \")", "Tony Blue");
+
+  // Deletions.
+  ExecuteExpectString("Stuff(\"A\", 1, 0)", "A");
+  ExecuteExpectString("Stuff(\"A\", 1, 1)", "");
+  ExecuteExpectString("Stuff(\"ABCDEFGH\", 4, 2)", "ABCFGH");
+  ExecuteExpectString("Stuff(\"ABCDEFGH\", 7, 2)", "ABCDEF");
+
+  // Test index clamping.
+  ExecuteExpectString("Stuff(\"ABCDEFGH\", -400, 400)", "");
+
+  // Need significant amount of text to test start + count overflow due to
+  // intermediate float representation of count not being able to hold
+  // INT_MAX.
+  ExecuteExpectString(
+      "Stuff(\""
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "\", 133, 2147483520)",
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900"
+      "abcd");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Substr) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
   // Test wrong number of parameters.
-  EXPECT_FALSE(Execute("Substr()"));
-  EXPECT_FALSE(Execute("Substr(1)"));
-  EXPECT_FALSE(Execute("Substr(1, 2)"));
-  EXPECT_FALSE(Execute("Substr(1, 2, 3, 4)"));
+  ExecuteExpectError("Substr()");
+  ExecuteExpectError("Substr(1)");
+  ExecuteExpectError("Substr(1, 2)");
+  ExecuteExpectError("Substr(1, 2, 3, 4)");
 
   // Test null input.
-  EXPECT_TRUE(ExecuteExpectNull("Substr(null, 0, 4)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", null, 4)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", 0, null)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(null, null, 4)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(null, 0, null)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", null, null)"));
-  EXPECT_TRUE(ExecuteExpectNull("Substr(null, null, null)"));
+  ExecuteExpectNull("Substr(null, 0, 4)");
+  ExecuteExpectNull("Substr(\"ABCDEFG\", null, 4)");
+  ExecuteExpectNull("Substr(\"ABCDEFG\", 0, null)");
+  ExecuteExpectNull("Substr(null, null, 4)");
+  ExecuteExpectNull("Substr(null, 0, null)");
+  ExecuteExpectNull("Substr(\"ABCDEFG\", null, null)");
+  ExecuteExpectNull("Substr(null, null, null)");
 
-  struct {
-    const char* program;
-    const char* result;
-  } static const kTests[] = {{"Substr(\"ABCDEFG\", -1, 4)", "ABCD"},
-                             {"Substr(\"ABCDEFG\", 0, 4)", "ABCD"},
-                             {"Substr(\"ABCDEFG\", 3, 4)", "CDEF"},
-                             {"Substr(\"ABCDEFG\", 4, 4)", "DEFG"},
-                             {"Substr(\"ABCDEFG\", 5, 4)", "EFG"},
-                             {"Substr(\"ABCDEFG\", 6, 4)", "FG"},
-                             {"Substr(\"ABCDEFG\", 7, 4)", "G"},
-                             {"Substr(\"ABCDEFG\", 8, 4)", ""},
-                             {"Substr(\"ABCDEFG\", 5, -1)", ""},
-                             {"Substr(\"ABCDEFG\", 5, 0)", ""},
-                             {"Substr(\"ABCDEFG\", 5, 1)", "E"},
-                             {"Substr(\"abcdefghi\", 5, 3)", "efg"},
-                             {"Substr(3214, 2, 1)", "2"},
-                             {"Substr(\"21 Waterloo St.\", 4, 5)", "Water"}};
-
-  for (const auto& test : kTests) {
-    EXPECT_TRUE(Execute(test.program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(test.result, value->ToString().c_str())
-        << "Program: " << test.program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Substr(\"ABCDEFG\", -1, 4)", "ABCD");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 0, 4)", "ABCD");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 3, 4)", "CDEF");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 4, 4)", "DEFG");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 5, 4)", "EFG");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 6, 4)", "FG");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 7, 4)", "G");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 8, 4)", "");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 5, -1)", "");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 5, 0)", "");
+  ExecuteExpectString("Substr(\"ABCDEFG\", 5, 1)", "E");
+  ExecuteExpectString("Substr(\"abcdefghi\", 5, 3)", "efg");
+  ExecuteExpectString("Substr(3214, 2, 1)", "2");
+  ExecuteExpectString("Substr(\"21 Waterloo St.\", 4, 5)", "Water");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Uuid) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
   EXPECT_TRUE(Execute("Uuid()"));
 
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsString());
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
+  v8::Local<v8::Value> value = GetValue();
+  EXPECT_TRUE(fxv8::IsString(value));
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Upper) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {{"Upper(\"abc\")", "ABC"},
-               {"Upper(\"21 Main St.\")", "21 MAIN ST."},
-               {"Upper(15)", "15"}};
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
-
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  ExecuteExpectString("Upper(\"abc\")", "ABC");
+  ExecuteExpectString("Upper(\"21 Main St.\")", "21 MAIN ST.");
+  ExecuteExpectString("Upper(15)", "15");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, WordNum) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  struct {
-    const char* program;
-    const char* result;
-  } tests[] = {
-      // {"WordNum(123.45)",
-      //  "One Hundred and Twenty-three"},  // This looks like it's wrong in the
-      //                                    // Formcalc document.
-      // {"WordNum(123.45, 1)", "One Hundred and Twenty-three Dollars"},
-      {"WordNum(1154.67, 2)",
-       "One Thousand One Hundred Fifty-four Dollars And Sixty-seven Cents"},
-      {"WordNum(43, 2)", "Forty-three Dollars And Zero Cents"}};
+  // Wrong number of parameters.
+  ExecuteExpectError("WordNum()");
+  ExecuteExpectError("WordNum(1, 2, 3, 4)");
 
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_TRUE(Execute(tests[i].program));
+  // Normal format codes.
+  ExecuteExpectString("WordNum(123.45)", "One Hundred Twenty-three");
+  ExecuteExpectString("WordNum(123.45, 0)", "One Hundred Twenty-three");
+  ExecuteExpectString("WordNum(123.45, 1)", "One Hundred Twenty-three Dollars");
+  ExecuteExpectString("WordNum(123.45, 2)",
+                      "One Hundred Twenty-three Dollars And Forty-five Cents");
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString());
-    EXPECT_STREQ(tests[i].result, value->ToString().c_str())
-        << "Program: " << tests[i].program << " Result: '" << value->ToString()
-        << "'";
-  }
+  // Invalid format code.
+  ExecuteExpectString("WordNum(123.45, -1)", "");
+  ExecuteExpectString("WordNum(123.45, 3)", "");
+
+  // Locale string is ignored.
+  ExecuteExpectString("WordNum(123.45, 0, \"zh_CN\")",
+                      "One Hundred Twenty-three");
+
+  // Zero (and near zero) values.
+  ExecuteExpectString("WordNum(0, 0)", "Zero");
+  ExecuteExpectString("WordNum(0, 1)", "Zero Dollars");
+  ExecuteExpectString("WordNum(0, 2)", "Zero Dollars And Zero Cents");
+  ExecuteExpectString("WordNum(0.12, 0)", "Zero");
+  ExecuteExpectString("WordNum(0.12, 1)", "Zero Dollars");
+  ExecuteExpectString("WordNum(0.12, 2)", "Zero Dollars And Twelve Cents");
+
+  // Negative values.
+  ExecuteExpectString("WordNum(-1, 0)", "*");
+  ExecuteExpectString("WordNum(-1, 1)", "*");
+  ExecuteExpectString("WordNum(-1, 2)", "*");
+
+  // Test larger values
+  // TODO(tsepez): check on "Thousand Zero"
+  ExecuteExpectString("WordNum(1.234e+6)",
+                      "One Million Two Hundred Thirty-four Thousand Zero");
+
+  // TODO(tsepez): check on "Zero Thousand Zero"
+  ExecuteExpectString(
+      "WordNum(1.234e+9)",
+      "One Billion Two Hundred Thirty-four Million Zero Thousand Zero");
+
+  // TODO(tsepez): check on "Zero Million"
+  ExecuteExpectString(
+      "WordNum(1.234e+12)",
+      "One Trillion Two Hundred Thirty-four Billion Zero Million Nineteen "
+      "Thousand Four Hundred Fifty-six");
+
+  ExecuteExpectString(
+      "WordNum(1.234e+15)",
+      "One Thousand Two Hundred Thirty-three Trillion Nine Hundred Ninety-nine "
+      "Billion Nine Hundred Thirty-eight Million Seven Hundred Fifteen "
+      "Thousand "
+      "Six Hundred Forty-eight");
+
+  // Out-of-range.
+  ExecuteExpectString("WordNum(1.234e+18)", "*");
+  ExecuteExpectString("WordNum(1.234e+21)", "*");
+  ExecuteExpectString("WordNum(1.234e+24)", "*");
+  ExecuteExpectString("WordNum(1.234e+30)", "*");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Get) {
-  // TODO(dsinclair): Is this supported?
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  ExecuteExpectString("Get(\"https://example.com\")", "<body>secrets</body>");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Post) {
-  // TODO(dsinclair): Is this supported?
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  ExecuteExpectString(
+      "Post(\"http://example.com\", \"secret stuff\", \"text/plain\")",
+      "posted");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Put) {
-  // TODO(dsinclair): Is this supported?
+  ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
+  ExecuteExpectString("Put(\"http://example.com\", \"secret stuff\")", "");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, InvalidFunctions) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  const char* const tests[] = {
-      "F()",
-      "()",
-      "()()()",
-      "Round(2.0)()",
-  };
-
-  for (size_t i = 0; i < FX_ArraySize(tests); ++i) {
-    EXPECT_FALSE(ExecuteSilenceFailure(tests[i]));
-  }
+  EXPECT_FALSE(ExecuteSilenceFailure("F()"));
+  EXPECT_FALSE(ExecuteSilenceFailure("()"));
+  EXPECT_FALSE(ExecuteSilenceFailure("()()()"));
+  EXPECT_FALSE(ExecuteSilenceFailure("Round(2.0)()"));
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, MethodCall) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
   const char test[] = {"$form.form1.TextField11.getAttribute(\"h\")"};
-  EXPECT_TRUE(Execute(test));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsString());
-  EXPECT_STREQ("12.7mm", value->ToString().c_str());
+  ExecuteExpectString(test, "12.7mm");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, GetXFAEventChange) {
@@ -1546,15 +1034,10 @@
   params.m_wsChange = L"changed";
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   const char test[] = {"xfa.event.change"};
-  EXPECT_TRUE(Execute(test));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsString());
-  EXPECT_STREQ("changed", value->ToString().c_str());
-  context->SetEventParam(nullptr);
+  ExecuteExpectString(test, "changed");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventChange) {
@@ -1562,12 +1045,11 @@
 
   CXFA_EventParam params;
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   const char test[] = {"xfa.event.change = \"changed\""};
   EXPECT_TRUE(Execute(test));
   EXPECT_EQ(L"changed", params.m_wsChange);
-  context->SetEventParam(nullptr);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventFullTextFails) {
@@ -1577,12 +1059,11 @@
   params.m_wsFullText = L"Original Full Text";
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   const char test[] = {"xfa.event.fullText = \"Changed Full Text\""};
   EXPECT_TRUE(Execute(test));
   EXPECT_EQ(L"Original Full Text", params.m_wsFullText);
-  context->SetEventParam(nullptr);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, EventChangeSelection) {
@@ -1594,7 +1075,7 @@
   params.m_iSelEnd = 3;
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   // Moving end to start works fine.
   EXPECT_TRUE(Execute("xfa.event.selEnd = \"1\""));
@@ -1637,8 +1118,6 @@
   EXPECT_TRUE(Execute("xfa.event.selStart = \"20\""));
   EXPECT_EQ(4, params.m_iSelStart);
   EXPECT_EQ(4, params.m_iSelEnd);
-
-  context->SetEventParam(nullptr);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, XFAEventCancelAction) {
@@ -1648,18 +1127,10 @@
   params.m_bCancelAction = false;
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
-
-  EXPECT_TRUE(Execute("xfa.event.cancelAction"));
-
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsBoolean());
-  EXPECT_FALSE(value->ToBoolean());
-
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
+  ExecuteExpectBool("xfa.event.cancelAction", false);
   EXPECT_TRUE(Execute("xfa.event.cancelAction = \"true\""));
   EXPECT_TRUE(params.m_bCancelAction);
-
-  context->SetEventParam(nullptr);
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, ComplexTextChangeEvent) {
@@ -1672,7 +1143,7 @@
   params.m_iSelEnd = 3;
 
   CFXJSE_Engine* context = GetScriptContext();
-  context->SetEventParam(&params);
+  CFXJSE_Engine::EventParamScope event_scope(context, nullptr, &params);
 
   EXPECT_EQ(L"abcd", params.m_wsPrevText);
   EXPECT_EQ(L"agd", params.GetNewText());
@@ -1697,13 +1168,10 @@
   EXPECT_EQ(L"axyzbcd", params.GetNewText());
   EXPECT_EQ(1, params.m_iSelStart);
   EXPECT_EQ(1, params.m_iSelEnd);
-
-  context->SetEventParam(nullptr);
 }
 
 // Should not crash.
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, BUG_1223) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
-
-  EXPECT_FALSE(Execute("!.somExpression=0"));
+  EXPECT_TRUE(Execute("!.somExpression=0"));
 }
diff --git a/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp b/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp
new file mode 100644
index 0000000..2500e3f
--- /dev/null
+++ b/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp
@@ -0,0 +1,74 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/xfa/cfxjse_formcalc_context.h"
+
+#include "core/fxcrt/bytestring.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(FormCalcContextTest, GenerateSomExpression) {
+  ByteString result =
+      CFXJSE_FormCalcContext::GenerateSomExpression("", 0, 0, /*bIsStar=*/true);
+  EXPECT_EQ(result, "[*]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("foo", 0, 0,
+                                                         /*bIsStar=*/true);
+  EXPECT_EQ(result, "foo[*]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("foo", 0, 0,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "foo");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("fu", 1, 0,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "fu[0]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("food", 1, 99,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "food[99]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("foot", 1, -65,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "foot[-65]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("football", 2, 0,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "football[0]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("foosball", 2, 123,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "foosball[+123]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("bar", 2, -654,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "bar[-654]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("barb", 2, 2147483647,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "barb[+2147483647]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression(
+      "bart", 2, -2147483648, /*bIsStar=*/false);
+  EXPECT_EQ(result, "bart[-0]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("bark", 3, 0,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "bark[0]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("bard", 3, 357,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "bard[-357]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("bars", 3, -9876,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "bars[9876]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression("cars", 3, 2147483647,
+                                                         /*bIsStar=*/false);
+  EXPECT_EQ(result, "cars[-2147483647]");
+
+  result = CFXJSE_FormCalcContext::GenerateSomExpression(
+      "mars", 3, -2147483648, /*bIsStar=*/false);
+  EXPECT_EQ(result, "mars[0]");
+}
diff --git a/fxjs/xfa/cfxjse_isolatetracker.cpp b/fxjs/xfa/cfxjse_isolatetracker.cpp
index 6a29e44..939553d 100644
--- a/fxjs/xfa/cfxjse_isolatetracker.cpp
+++ b/fxjs/xfa/cfxjse_isolatetracker.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,20 +6,38 @@
 
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
 
+#include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_runtimedata.h"
 
 CFXJSE_ScopeUtil_IsolateHandle::CFXJSE_ScopeUtil_IsolateHandle(
     v8::Isolate* pIsolate)
-    : m_iscope(pIsolate), m_hscope(pIsolate) {}
+    : isolate_scope_(pIsolate), handle_scope_(pIsolate) {}
 
 CFXJSE_ScopeUtil_IsolateHandle::~CFXJSE_ScopeUtil_IsolateHandle() = default;
 
+CFXJSE_ScopeUtil_Context::CFXJSE_ScopeUtil_Context(CFXJSE_Context* pContext)
+    : context_scope_(pContext->GetContext()) {}
+
+CFXJSE_ScopeUtil_Context::~CFXJSE_ScopeUtil_Context() = default;
+
+CFXJSE_ScopeUtil_IsolateHandleContext::CFXJSE_ScopeUtil_IsolateHandleContext(
+    CFXJSE_Context* pContext)
+    : isolate_handle_(pContext->GetIsolate()), context_(pContext) {}
+
+CFXJSE_ScopeUtil_IsolateHandleContext::
+    ~CFXJSE_ScopeUtil_IsolateHandleContext() = default;
+
+CFXJSE_ScopeUtil_RootContext::CFXJSE_ScopeUtil_RootContext(
+    v8::Isolate* pIsolate)
+    : context_scope_(v8::Local<v8::Context>::New(
+          pIsolate,
+          CFXJSE_RuntimeData::Get(pIsolate)->GetRootContext())) {}
+
+CFXJSE_ScopeUtil_RootContext::~CFXJSE_ScopeUtil_RootContext() = default;
+
 CFXJSE_ScopeUtil_IsolateHandleRootContext::
     CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate)
-    : CFXJSE_ScopeUtil_IsolateHandle(pIsolate),
-      m_cscope(v8::Local<v8::Context>::New(
-          pIsolate,
-          CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext)) {}
+    : isolate_handle_(pIsolate), root_context_(pIsolate) {}
 
 CFXJSE_ScopeUtil_IsolateHandleRootContext::
     ~CFXJSE_ScopeUtil_IsolateHandleRootContext() = default;
diff --git a/fxjs/xfa/cfxjse_isolatetracker.h b/fxjs/xfa/cfxjse_isolatetracker.h
index 020142d..7353ac0 100644
--- a/fxjs/xfa/cfxjse_isolatetracker.h
+++ b/fxjs/xfa/cfxjse_isolatetracker.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,16 @@
 #ifndef FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
 #define FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
 
-#include "v8/include/v8.h"
+#include "core/fxcrt/fx_memory.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+
+class CFXJSE_Context;
 
 class CFXJSE_ScopeUtil_IsolateHandle {
  public:
+  FX_STACK_ALLOCATED();
+
   explicit CFXJSE_ScopeUtil_IsolateHandle(v8::Isolate* pIsolate);
   CFXJSE_ScopeUtil_IsolateHandle(const CFXJSE_ScopeUtil_IsolateHandle&) =
       delete;
@@ -19,16 +25,57 @@
   ~CFXJSE_ScopeUtil_IsolateHandle();
 
  private:
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  v8::Isolate::Scope m_iscope;
-  v8::HandleScope m_hscope;
+  v8::Isolate::Scope isolate_scope_;
+  v8::HandleScope handle_scope_;
 };
 
-class CFXJSE_ScopeUtil_IsolateHandleRootContext final
-    : public CFXJSE_ScopeUtil_IsolateHandle {
+class CFXJSE_ScopeUtil_Context {
  public:
+  FX_STACK_ALLOCATED();
+
+  explicit CFXJSE_ScopeUtil_Context(CFXJSE_Context* pContext);
+  CFXJSE_ScopeUtil_Context(const CFXJSE_ScopeUtil_Context&) = delete;
+  CFXJSE_ScopeUtil_Context& operator=(const CFXJSE_ScopeUtil_Context&) = delete;
+  ~CFXJSE_ScopeUtil_Context();
+
+ private:
+  v8::Context::Scope context_scope_;
+};
+
+class CFXJSE_ScopeUtil_IsolateHandleContext {
+ public:
+  FX_STACK_ALLOCATED();
+
+  explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext);
+  CFXJSE_ScopeUtil_IsolateHandleContext(
+      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
+  CFXJSE_ScopeUtil_IsolateHandleContext& operator=(
+      const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete;
+  ~CFXJSE_ScopeUtil_IsolateHandleContext();
+
+ private:
+  CFXJSE_ScopeUtil_IsolateHandle isolate_handle_;
+  CFXJSE_ScopeUtil_Context context_;
+};
+
+class CFXJSE_ScopeUtil_RootContext {
+ public:
+  FX_STACK_ALLOCATED();
+
+  explicit CFXJSE_ScopeUtil_RootContext(v8::Isolate* pIsolate);
+  CFXJSE_ScopeUtil_RootContext(const CFXJSE_ScopeUtil_RootContext&) = delete;
+  CFXJSE_ScopeUtil_RootContext& operator=(const CFXJSE_ScopeUtil_RootContext&) =
+      delete;
+  ~CFXJSE_ScopeUtil_RootContext();
+
+ private:
+  v8::Context::Scope context_scope_;
+};
+
+class CFXJSE_ScopeUtil_IsolateHandleRootContext {
+ public:
+  FX_STACK_ALLOCATED();
+
   explicit CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate);
   CFXJSE_ScopeUtil_IsolateHandleRootContext(
       const CFXJSE_ScopeUtil_IsolateHandleRootContext&) = delete;
@@ -37,10 +84,8 @@
   ~CFXJSE_ScopeUtil_IsolateHandleRootContext();
 
  private:
-  void* operator new(size_t size) = delete;
-  void operator delete(void*, size_t) = delete;
-
-  v8::Context::Scope m_cscope;
+  CFXJSE_ScopeUtil_IsolateHandle isolate_handle_;
+  CFXJSE_ScopeUtil_RootContext root_context_;
 };
 
 #endif  // FXJS_XFA_CFXJSE_ISOLATETRACKER_H_
diff --git a/fxjs/xfa/cfxjse_mapmodule.cpp b/fxjs/xfa/cfxjse_mapmodule.cpp
new file mode 100644
index 0000000..7e656b8
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule.cpp
@@ -0,0 +1,78 @@
+// Copyright 2020 The PDFium Authors
+// 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
+
+#include "fxjs/xfa/cfxjse_mapmodule.h"
+
+#include "third_party/base/containers/contains.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+
+CFXJSE_MapModule::CFXJSE_MapModule() = default;
+
+CFXJSE_MapModule::~CFXJSE_MapModule() = default;
+
+void CFXJSE_MapModule::SetValue(uint32_t key, int32_t value) {
+  m_StringMap.erase(key);
+  m_MeasurementMap.erase(key);
+  m_ValueMap[key] = value;
+}
+
+void CFXJSE_MapModule::SetString(uint32_t key, const WideString& wsString) {
+  m_ValueMap.erase(key);
+  m_MeasurementMap.erase(key);
+  m_StringMap[key] = wsString;
+}
+
+void CFXJSE_MapModule::SetMeasurement(uint32_t key,
+                                      const CXFA_Measurement& measurement) {
+  m_ValueMap.erase(key);
+  m_StringMap.erase(key);
+  m_MeasurementMap[key] = measurement;
+}
+
+absl::optional<int32_t> CFXJSE_MapModule::GetValue(uint32_t key) const {
+  auto it = m_ValueMap.find(key);
+  if (it == m_ValueMap.end())
+    return absl::nullopt;
+  return it->second;
+}
+
+absl::optional<WideString> CFXJSE_MapModule::GetString(uint32_t key) const {
+  auto it = m_StringMap.find(key);
+  if (it == m_StringMap.end())
+    return absl::nullopt;
+  return it->second;
+}
+
+absl::optional<CXFA_Measurement> CFXJSE_MapModule::GetMeasurement(
+    uint32_t key) const {
+  auto it = m_MeasurementMap.find(key);
+  if (it == m_MeasurementMap.end())
+    return absl::nullopt;
+  return it->second;
+}
+
+bool CFXJSE_MapModule::HasKey(uint32_t key) const {
+  return pdfium::Contains(m_ValueMap, key) ||
+         pdfium::Contains(m_StringMap, key) ||
+         pdfium::Contains(m_MeasurementMap, key);
+}
+
+void CFXJSE_MapModule::RemoveKey(uint32_t key) {
+  m_ValueMap.erase(key);
+  m_StringMap.erase(key);
+  m_MeasurementMap.erase(key);
+}
+
+void CFXJSE_MapModule::MergeDataFrom(const CFXJSE_MapModule* pSrc) {
+  for (const auto& pair : pSrc->m_ValueMap)
+    SetValue(pair.first, pair.second);
+
+  for (const auto& pair : pSrc->m_StringMap)
+    SetString(pair.first, pair.second);
+
+  for (const auto& pair : pSrc->m_MeasurementMap)
+    SetMeasurement(pair.first, pair.second);
+}
diff --git a/fxjs/xfa/cfxjse_mapmodule.h b/fxjs/xfa/cfxjse_mapmodule.h
new file mode 100644
index 0000000..9c75750
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule.h
@@ -0,0 +1,44 @@
+// Copyright 2020 The PDFium Authors
+// 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 FXJS_XFA_CFXJSE_MAPMODULE_H_
+#define FXJS_XFA_CFXJSE_MAPMODULE_H_
+
+#include <stdint.h>
+
+#include <map>
+
+#include "core/fxcrt/widestring.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class CXFA_Measurement;
+
+class CFXJSE_MapModule {
+ public:
+  CFXJSE_MapModule();
+  ~CFXJSE_MapModule();
+
+  CFXJSE_MapModule(const CFXJSE_MapModule& that) = delete;
+  CFXJSE_MapModule& operator=(const CFXJSE_MapModule& that) = delete;
+
+  void SetValue(uint32_t key, int32_t value);
+  void SetString(uint32_t key, const WideString& wsString);
+  void SetMeasurement(uint32_t key, const CXFA_Measurement& measurement);
+  absl::optional<int32_t> GetValue(uint32_t key) const;
+  absl::optional<WideString> GetString(uint32_t key) const;
+  absl::optional<CXFA_Measurement> GetMeasurement(uint32_t key) const;
+  bool HasKey(uint32_t key) const;
+  void RemoveKey(uint32_t key);
+  void MergeDataFrom(const CFXJSE_MapModule* pSrc);
+
+ private:
+  // keyed by result of GetMapKey_*().
+  std::map<uint32_t, int32_t> m_ValueMap;
+  std::map<uint32_t, WideString> m_StringMap;
+  std::map<uint32_t, CXFA_Measurement> m_MeasurementMap;
+};
+
+#endif  // FXJS_XFA_CFXJSE_MAPMODULE_H_
diff --git a/fxjs/xfa/cfxjse_mapmodule_unittest.cpp b/fxjs/xfa/cfxjse_mapmodule_unittest.cpp
new file mode 100644
index 0000000..3119b58
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule_unittest.cpp
@@ -0,0 +1,124 @@
+// Copyright 2020 The PDFium Authors
+// 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
+
+#include "fxjs/xfa/cfxjse_mapmodule.h"
+
+#include "core/fxcrt/fx_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+
+TEST(CFXJSEMapModule, EmptyModule) {
+  CFXJSE_MapModule module;
+  EXPECT_FALSE(module.HasKey(1));
+  EXPECT_FALSE(module.HasKey(2));
+  EXPECT_FALSE(module.HasKey(3));
+  EXPECT_FALSE(module.GetValue(1).has_value());
+  EXPECT_FALSE(module.GetString(2).has_value());
+  EXPECT_FALSE(module.GetMeasurement(3).has_value());
+}
+
+TEST(CFXJSEMapModule, InsertDelete) {
+  const int value = 101;
+  WideString str(L"foo");
+  CXFA_Measurement measure(L"1 pt");
+  CFXJSE_MapModule module;
+
+  module.SetValue(100, value);
+  module.SetString(200, str);
+  module.SetMeasurement(300, measure);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_TRUE(module.HasKey(200));
+  EXPECT_TRUE(module.HasKey(300));
+
+  EXPECT_EQ(module.GetValue(100).value(), value);
+  EXPECT_FALSE(module.GetString(100).has_value());
+  EXPECT_FALSE(module.GetMeasurement(100).has_value());
+
+  EXPECT_FALSE(module.GetValue(200).has_value());
+  EXPECT_EQ(module.GetString(200).value(), str);
+  EXPECT_FALSE(module.GetMeasurement(200).has_value());
+
+  EXPECT_FALSE(module.GetValue(300).has_value());
+  EXPECT_FALSE(module.GetString(300).has_value());
+  EXPECT_EQ(module.GetMeasurement(300).value().GetUnit(), measure.GetUnit());
+  EXPECT_EQ(module.GetMeasurement(300).value().GetValue(), measure.GetValue());
+
+  module.RemoveKey(100);
+  module.RemoveKey(200);
+  module.RemoveKey(300);
+  EXPECT_FALSE(module.HasKey(100));
+  EXPECT_FALSE(module.HasKey(200));
+  EXPECT_FALSE(module.HasKey(300));
+  EXPECT_FALSE(module.GetValue(100).has_value());
+  EXPECT_FALSE(module.GetString(200).has_value());
+  EXPECT_FALSE(module.GetMeasurement(200).has_value());
+}
+
+TEST(CFXJSEMapModule, KeyCollision) {
+  const int value = 37;
+  WideString str(L"foo");
+  CXFA_Measurement measure(L"1 pt");
+  CFXJSE_MapModule module;
+
+  module.SetValue(100, value);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_EQ(module.GetValue(100).value(), value);
+  EXPECT_FALSE(module.GetString(100).has_value());
+  EXPECT_FALSE(module.GetMeasurement(100).has_value());
+
+  module.SetString(100, str);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_FALSE(module.GetValue(100).has_value());
+  EXPECT_EQ(module.GetString(100).value(), str);
+  EXPECT_FALSE(module.GetMeasurement(100).has_value());
+
+  module.SetMeasurement(100, measure);
+  EXPECT_FALSE(module.GetValue(100).has_value());
+  EXPECT_FALSE(module.GetString(100).has_value());
+  EXPECT_EQ(module.GetMeasurement(100).value().GetUnit(), measure.GetUnit());
+
+  module.SetValue(100, value);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_EQ(module.GetValue(100).value(), value);
+  EXPECT_FALSE(module.GetString(100).has_value());
+  EXPECT_FALSE(module.GetMeasurement(100).has_value());
+}
+
+TEST(CFXJSEMapModule, MergeData) {
+  const int value1 = 42;
+  const int value2 = -1999;
+  WideString string1(L"foo");
+  WideString string2(L"foo");
+  CXFA_Measurement measure1(L"1 pt");
+  CXFA_Measurement measure2(L"2 mm");
+  CFXJSE_MapModule module1;
+  CFXJSE_MapModule module2;
+
+  module1.SetValue(100, value1);
+  module1.SetValue(101, value1);
+  module1.SetString(200, string1);
+  module1.SetString(201, string1);
+  module1.SetMeasurement(300, measure1);
+  module1.SetMeasurement(301, measure1);
+
+  module2.SetString(100, string2);
+  module2.SetMeasurement(200, measure2);
+  module2.SetValue(300, value2);
+
+  module1.MergeDataFrom(&module2);
+  EXPECT_EQ(module1.GetString(100).value(), string2);
+  EXPECT_EQ(module1.GetValue(101).value(), value1);
+  EXPECT_EQ(module1.GetMeasurement(200).value().GetUnit(), measure2.GetUnit());
+  EXPECT_EQ(module1.GetString(201).value(), string1);
+  EXPECT_EQ(module1.GetValue(300).value(), value2);
+  EXPECT_EQ(module1.GetMeasurement(301).value().GetUnit(), measure1.GetUnit());
+
+  // module2 is undisturbed.
+  EXPECT_EQ(module2.GetString(100).value(), string2);
+  EXPECT_EQ(module2.GetMeasurement(200).value().GetUnit(), measure2.GetUnit());
+  EXPECT_EQ(module2.GetValue(300).value(), value2);
+}
diff --git a/fxjs/xfa/cfxjse_nodehelper.cpp b/fxjs/xfa/cfxjse_nodehelper.cpp
new file mode 100644
index 0000000..de24aa4
--- /dev/null
+++ b/fxjs/xfa/cfxjse_nodehelper.cpp
@@ -0,0 +1,136 @@
+// Copyright 2014 The PDFium Authors
+// 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
+
+#include "fxjs/xfa/cfxjse_nodehelper.h"
+
+#include "core/fxcrt/fx_extension.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_localemgr.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/xfa_basic_data.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+CFXJSE_NodeHelper::CFXJSE_NodeHelper() = default;
+
+CFXJSE_NodeHelper::~CFXJSE_NodeHelper() = default;
+
+bool CFXJSE_NodeHelper::CreateNodeForCondition(const WideString& wsCondition) {
+  size_t szLen = wsCondition.GetLength();
+  WideString wsIndex(L"0");
+  bool bAll = false;
+  if (szLen == 0) {
+    m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne;
+    return false;
+  }
+  if (wsCondition[0] != '[')
+    return false;
+
+  size_t i = 1;
+  for (; i < szLen; ++i) {
+    wchar_t ch = wsCondition[i];
+    if (ch == ' ')
+      continue;
+
+    if (ch == '*')
+      bAll = true;
+    break;
+  }
+  if (bAll) {
+    wsIndex = L"1";
+    m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeAll;
+  } else {
+    m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne;
+    wsIndex = wsCondition.Substr(i, szLen - 1 - i);
+  }
+  int32_t iCount = wsIndex.GetInteger();
+  if (iCount < 0)
+    return false;
+
+  m_iCreateCount = iCount;
+  return true;
+}
+
+bool CFXJSE_NodeHelper::CreateNode(const WideString& wsName,
+                                   const WideString& wsCondition,
+                                   bool bLastNode,
+                                   CFXJSE_Engine* pScriptContext) {
+  if (!m_pCreateParent)
+    return false;
+
+  WideStringView wsNameView = wsName.AsStringView();
+  bool bIsClassName = false;
+  bool bResult = false;
+  if (!wsNameView.IsEmpty() && wsNameView[0] == '!') {
+    wsNameView = wsNameView.Last(wsNameView.GetLength() - 1);
+    m_pCreateParent = ToNode(
+        pScriptContext->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
+  }
+  if (!wsNameView.IsEmpty() && wsNameView[0] == '#') {
+    bIsClassName = true;
+    wsNameView = wsNameView.Last(wsNameView.GetLength() - 1);
+  }
+  if (wsNameView.IsEmpty())
+    return false;
+
+  if (m_iCreateCount == 0)
+    CreateNodeForCondition(wsCondition);
+
+  if (bIsClassName) {
+    XFA_Element eType = XFA_GetElementByName(wsNameView);
+    if (eType == XFA_Element::Unknown)
+      return false;
+
+    for (size_t i = 0; i < m_iCreateCount; ++i) {
+      CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eType);
+      if (pNewNode) {
+        m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr);
+        if (i == m_iCreateCount - 1) {
+          m_pCreateParent = pNewNode;
+        }
+        bResult = true;
+      }
+    }
+  } else {
+    XFA_Element eClassType = XFA_Element::DataGroup;
+    if (bLastNode) {
+      eClassType = m_eLastCreateType;
+    }
+    for (size_t i = 0; i < m_iCreateCount; ++i) {
+      CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eClassType);
+      if (pNewNode) {
+        pNewNode->JSObject()->SetAttributeByEnum(XFA_Attribute::Name,
+                                                 WideString(wsNameView), false);
+        pNewNode->CreateXMLMappingNode();
+        m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr);
+        if (i == m_iCreateCount - 1) {
+          m_pCreateParent = pNewNode;
+        }
+        bResult = true;
+      }
+    }
+  }
+  if (!bResult)
+    m_pCreateParent = nullptr;
+
+  return bResult;
+}
+
+void CFXJSE_NodeHelper::SetCreateNodeType(CXFA_Node* refNode) {
+  if (!refNode)
+    return;
+
+  if (refNode->GetElementType() == XFA_Element::Subform) {
+    m_eLastCreateType = XFA_Element::DataGroup;
+  } else if (refNode->GetElementType() == XFA_Element::Field) {
+    m_eLastCreateType = XFA_FieldIsMultiListBox(refNode)
+                            ? XFA_Element::DataGroup
+                            : XFA_Element::DataValue;
+  } else if (refNode->GetElementType() == XFA_Element::ExclGroup) {
+    m_eLastCreateType = XFA_Element::DataValue;
+  }
+}
diff --git a/fxjs/xfa/cfxjse_nodehelper.h b/fxjs/xfa/cfxjse_nodehelper.h
new file mode 100644
index 0000000..7afc10c
--- /dev/null
+++ b/fxjs/xfa/cfxjse_nodehelper.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The PDFium Authors
+// 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 FXJS_XFA_CFXJSE_NODEHELPER_H_
+#define FXJS_XFA_CFXJSE_NODEHELPER_H_
+
+#include "core/fxcrt/widestring.h"
+#include "fxjs/xfa/cfxjse_engine.h"
+#include "v8/include/cppgc/persistent.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+class CXFA_Node;
+
+class CFXJSE_NodeHelper {
+ public:
+  CFXJSE_NodeHelper();
+  ~CFXJSE_NodeHelper();
+
+  bool CreateNode(const WideString& wsName,
+                  const WideString& wsCondition,
+                  bool bLastNode,
+                  CFXJSE_Engine* pScriptContext);
+  bool CreateNodeForCondition(const WideString& wsCondition);
+  void SetCreateNodeType(CXFA_Node* refNode);
+
+  XFA_Element m_eLastCreateType = XFA_Element::DataValue;
+  CFXJSE_Engine::ResolveResult::Type m_iCreateFlag =
+      CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne;
+  size_t m_iCreateCount = 0;
+  int32_t m_iCurAllStart = -1;
+  cppgc::Persistent<CXFA_Node> m_pCreateParent;
+  cppgc::Persistent<CXFA_Node> m_pAllStartParent;
+};
+
+#endif  // FXJS_XFA_CFXJSE_NODEHELPER_H_
diff --git a/fxjs/xfa/cfxjse_resolveprocessor.cpp b/fxjs/xfa/cfxjse_resolveprocessor.cpp
index 89883d6..bfe571c 100644
--- a/fxjs/xfa/cfxjse_resolveprocessor.cpp
+++ b/fxjs/xfa/cfxjse_resolveprocessor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,114 +12,87 @@
 
 #include "core/fxcrt/fx_extension.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_nodehelper.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/cxfa_nodehelper.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
-namespace {
-
-void DoPredicateFilter(WideString wsCondition,
-                       size_t iFoundCount,
-                       CFXJSE_ResolveNodeData* pRnd) {
-  ASSERT(iFoundCount == pRnd->m_Objects.size());
-  WideString wsExpression;
-  CXFA_Script::Type eLangType = CXFA_Script::Type::Unknown;
-  if (wsCondition.First(2).EqualsASCII(".[") && wsCondition.Back() == L']')
-    eLangType = CXFA_Script::Type::Formcalc;
-  else if (wsCondition.First(2).EqualsASCII(".(") && wsCondition.Back() == L')')
-    eLangType = CXFA_Script::Type::Javascript;
-  else
-    return;
-
-  wsExpression = wsCondition.Substr(2, wsCondition.GetLength() - 3);
-  for (size_t i = iFoundCount; i > 0; --i) {
-    auto pRetValue =
-        pdfium::MakeUnique<CFXJSE_Value>(pRnd->m_pSC->GetIsolate());
-    bool bRet =
-        pRnd->m_pSC->RunScript(eLangType, wsExpression.AsStringView(),
-                               pRetValue.get(), pRnd->m_Objects[i - 1].Get());
-    if (!bRet || !pRetValue->ToBoolean())
-      pRnd->m_Objects.erase(pRnd->m_Objects.begin() + i - 1);
-  }
-}
-
-}  // namespace
-
-CFXJSE_ResolveProcessor::CFXJSE_ResolveProcessor()
-    : m_pNodeHelper(pdfium::MakeUnique<CXFA_NodeHelper>()) {}
+CFXJSE_ResolveProcessor::CFXJSE_ResolveProcessor(CFXJSE_Engine* pEngine,
+                                                 CFXJSE_NodeHelper* pHelper)
+    : m_pEngine(pEngine), m_pNodeHelper(pHelper) {}
 
 CFXJSE_ResolveProcessor::~CFXJSE_ResolveProcessor() = default;
 
-bool CFXJSE_ResolveProcessor::Resolve(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::Resolve(v8::Isolate* pIsolate, NodeData& rnd) {
   if (!rnd.m_CurObject)
     return false;
 
   if (!rnd.m_CurObject->IsNode()) {
-    if (rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) {
-      return ResolveForAttributeRs(rnd.m_CurObject.Get(), rnd,
+    if (rnd.m_dwStyles & XFA_ResolveFlag::kAttributes) {
+      return ResolveForAttributeRs(rnd.m_CurObject, &rnd.m_Result,
                                    rnd.m_wsName.AsStringView());
     }
     return false;
   }
-  if (rnd.m_dwStyles & XFA_RESOLVENODE_AnyChild)
-    return ResolveAnyChild(rnd);
+  if (rnd.m_dwStyles & XFA_ResolveFlag::kAnyChild)
+    return ResolveAnyChild(pIsolate, rnd);
 
   if (rnd.m_wsName.GetLength()) {
     wchar_t wch = rnd.m_wsName[0];
     switch (wch) {
       case '$':
-        return ResolveDollar(rnd);
+        return ResolveDollar(pIsolate, rnd);
       case '!':
-        return ResolveExcalmatory(rnd);
+        return ResolveExcalmatory(pIsolate, rnd);
       case '#':
-        return ResolveNumberSign(rnd);
+        return ResolveNumberSign(pIsolate, rnd);
       case '*':
         return ResolveAsterisk(rnd);
       // TODO(dsinclair): We could probably remove this.
       case '.':
-        return ResolveAnyChild(rnd);
+        return ResolveAnyChild(pIsolate, rnd);
       default:
         break;
     }
   }
   if (rnd.m_uHashName == XFA_HASHCODE_This && rnd.m_nLevel == 0) {
-    rnd.m_Objects.emplace_back(rnd.m_pSC->GetThisObject());
+    rnd.m_Result.objects.emplace_back(m_pEngine->GetThisObject());
     return true;
   }
   if (rnd.m_CurObject->GetElementType() == XFA_Element::Xfa) {
     CXFA_Object* pObjNode =
-        rnd.m_pSC->GetDocument()->GetXFAObject(rnd.m_uHashName);
+        m_pEngine->GetDocument()->GetXFAObject(rnd.m_uHashName);
     if (pObjNode) {
-      rnd.m_Objects.emplace_back(pObjNode);
+      rnd.m_Result.objects.emplace_back(pObjNode);
     } else if (rnd.m_uHashName == XFA_HASHCODE_Xfa) {
-      rnd.m_Objects.push_back(rnd.m_CurObject);
-    } else if ((rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) &&
-               ResolveForAttributeRs(rnd.m_CurObject.Get(), rnd,
+      rnd.m_Result.objects.emplace_back(rnd.m_CurObject);
+    } else if ((rnd.m_dwStyles & XFA_ResolveFlag::kAttributes) &&
+               ResolveForAttributeRs(rnd.m_CurObject, &rnd.m_Result,
                                      rnd.m_wsName.AsStringView())) {
       return true;
     }
-    if (!rnd.m_Objects.empty())
-      FilterCondition(rnd.m_wsCondition, &rnd);
+    if (!rnd.m_Result.objects.empty())
+      FilterCondition(pIsolate, rnd.m_wsCondition, &rnd);
 
-    return !rnd.m_Objects.empty();
+    return !rnd.m_Result.objects.empty();
   }
-  if (!ResolveNormal(rnd) && rnd.m_uHashName == XFA_HASHCODE_Xfa)
-    rnd.m_Objects.emplace_back(rnd.m_pSC->GetDocument()->GetRoot());
+  if (!ResolveNormal(pIsolate, rnd) && rnd.m_uHashName == XFA_HASHCODE_Xfa)
+    rnd.m_Result.objects.emplace_back(m_pEngine->GetDocument()->GetRoot());
 
-  return !rnd.m_Objects.empty();
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveAnyChild(CFXJSE_ResolveNodeData& rnd) {
-  CXFA_Node* pParent = ToNode(rnd.m_CurObject.Get());
+bool CFXJSE_ResolveProcessor::ResolveAnyChild(v8::Isolate* pIsolate,
+                                              NodeData& rnd) {
+  CXFA_Node* pParent = ToNode(rnd.m_CurObject);
   if (!pParent)
     return false;
 
@@ -134,128 +107,136 @@
     return false;
 
   if (wsCondition.IsEmpty()) {
-    rnd.m_Objects.emplace_back(pChild);
+    rnd.m_Result.objects.emplace_back(pChild);
     return true;
   }
 
   std::vector<CXFA_Node*> nodes;
-  for (const auto& pObject : rnd.m_Objects)
+  for (const auto& pObject : rnd.m_Result.objects)
     nodes.push_back(pObject->AsNode());
 
   std::vector<CXFA_Node*> siblings = pChild->GetSiblings(bClassName);
   nodes.insert(nodes.end(), siblings.begin(), siblings.end());
-  rnd.m_Objects =
-      std::vector<UnownedPtr<CXFA_Object>>(nodes.begin(), nodes.end());
-  FilterCondition(wsCondition, &rnd);
-  return !rnd.m_Objects.empty();
+  rnd.m_Result.objects =
+      std::vector<cppgc::Member<CXFA_Object>>(nodes.begin(), nodes.end());
+  FilterCondition(pIsolate, wsCondition, &rnd);
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveDollar(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::ResolveDollar(v8::Isolate* pIsolate,
+                                            NodeData& rnd) {
   WideString wsName = rnd.m_wsName;
   WideString wsCondition = rnd.m_wsCondition;
-  int32_t iNameLen = wsName.GetLength();
-  if (iNameLen == 1) {
-    rnd.m_Objects.push_back(rnd.m_CurObject);
+  size_t nNameLen = wsName.GetLength();
+  if (nNameLen == 1) {
+    rnd.m_Result.objects.emplace_back(rnd.m_CurObject);
     return true;
   }
   if (rnd.m_nLevel > 0)
     return false;
 
+  CXFA_Document* pDocument = m_pEngine->GetDocument();
   XFA_HashCode dwNameHash = static_cast<XFA_HashCode>(
-      FX_HashCode_GetW(wsName.AsStringView().Last(iNameLen - 1), false));
+      FX_HashCode_GetW(wsName.AsStringView().Last(nNameLen - 1)));
   if (dwNameHash == XFA_HASHCODE_Xfa) {
-    rnd.m_Objects.emplace_back(rnd.m_pSC->GetDocument()->GetRoot());
+    rnd.m_Result.objects.emplace_back(pDocument->GetRoot());
   } else {
-    CXFA_Object* pObjNode = rnd.m_pSC->GetDocument()->GetXFAObject(dwNameHash);
+    CXFA_Object* pObjNode = pDocument->GetXFAObject(dwNameHash);
     if (pObjNode)
-      rnd.m_Objects.emplace_back(pObjNode);
+      rnd.m_Result.objects.emplace_back(pObjNode);
   }
-  if (!rnd.m_Objects.empty())
-    FilterCondition(wsCondition, &rnd);
-  return !rnd.m_Objects.empty();
+  if (!rnd.m_Result.objects.empty())
+    FilterCondition(pIsolate, wsCondition, &rnd);
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::ResolveExcalmatory(v8::Isolate* pIsolate,
+                                                 NodeData& rnd) {
   if (rnd.m_nLevel > 0)
     return false;
 
   CXFA_Node* datasets =
-      ToNode(rnd.m_pSC->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
+      ToNode(m_pEngine->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets));
   if (!datasets)
     return false;
 
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  NodeData rndFind;
   rndFind.m_CurObject = datasets;
   rndFind.m_wsName = rnd.m_wsName.Last(rnd.m_wsName.GetLength() - 1);
   rndFind.m_uHashName = static_cast<XFA_HashCode>(
-      FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false));
+      FX_HashCode_GetW(rndFind.m_wsName.AsStringView()));
   rndFind.m_nLevel = rnd.m_nLevel + 1;
-  rndFind.m_dwStyles = XFA_RESOLVENODE_Children;
+  rndFind.m_dwStyles = XFA_ResolveFlag::kChildren;
   rndFind.m_wsCondition = rnd.m_wsCondition;
-  Resolve(rndFind);
+  Resolve(pIsolate, rndFind);
 
-  rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                       rndFind.m_Objects.end());
-  return !rnd.m_Objects.empty();
+  rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                              rndFind.m_Result.objects.begin(),
+                              rndFind.m_Result.objects.end());
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveNumberSign(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::ResolveNumberSign(v8::Isolate* pIsolate,
+                                                NodeData& rnd) {
   WideString wsName = rnd.m_wsName.Last(rnd.m_wsName.GetLength() - 1);
   WideString wsCondition = rnd.m_wsCondition;
-  CXFA_Node* curNode = ToNode(rnd.m_CurObject.Get());
-  if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView()))
+  CXFA_Node* curNode = ToNode(rnd.m_CurObject);
+  if (ResolveForAttributeRs(curNode, &rnd.m_Result, wsName.AsStringView()))
     return true;
 
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  NodeData rndFind;
   rndFind.m_nLevel = rnd.m_nLevel + 1;
   rndFind.m_dwStyles = rnd.m_dwStyles;
-  rndFind.m_dwStyles |= XFA_RESOLVENODE_TagName;
-  rndFind.m_dwStyles &= ~XFA_RESOLVENODE_Attributes;
+  rndFind.m_dwStyles |= XFA_ResolveFlag::kTagName;
+  rndFind.m_dwStyles.Clear(XFA_ResolveFlag::kAttributes);
   rndFind.m_wsName = std::move(wsName);
   rndFind.m_uHashName = static_cast<XFA_HashCode>(
-      FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false));
+      FX_HashCode_GetW(rndFind.m_wsName.AsStringView()));
   rndFind.m_wsCondition = wsCondition;
   rndFind.m_CurObject = curNode;
-  ResolveNormal(rndFind);
-  if (rndFind.m_Objects.empty())
+  ResolveNormal(pIsolate, rndFind);
+  if (rndFind.m_Result.objects.empty())
     return false;
 
   if (wsCondition.IsEmpty() &&
-      pdfium::ContainsValue(rndFind.m_Objects, curNode)) {
-    rnd.m_Objects.emplace_back(curNode);
+      pdfium::Contains(rndFind.m_Result.objects, curNode)) {
+    rnd.m_Result.objects.emplace_back(curNode);
   } else {
-    rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                         rndFind.m_Objects.end());
+    rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                rndFind.m_Result.objects.begin(),
+                                rndFind.m_Result.objects.end());
   }
-  return !rnd.m_Objects.empty();
+  return !rnd.m_Result.objects.empty();
 }
 
-bool CFXJSE_ResolveProcessor::ResolveForAttributeRs(CXFA_Object* curNode,
-                                                    CFXJSE_ResolveNodeData& rnd,
-                                                    WideStringView strAttr) {
-  Optional<XFA_SCRIPTATTRIBUTEINFO> info =
+bool CFXJSE_ResolveProcessor::ResolveForAttributeRs(
+    CXFA_Object* curNode,
+    CFXJSE_Engine::ResolveResult* rnd,
+    WideStringView strAttr) {
+  absl::optional<XFA_SCRIPTATTRIBUTEINFO> info =
       XFA_GetScriptAttributeByName(curNode->GetElementType(), strAttr);
   if (!info.has_value())
     return false;
 
-  rnd.m_ScriptAttribute = info.value();
-  rnd.m_Objects.emplace_back(curNode);
-  rnd.m_dwFlag = XFA_ResolveNode_RSType_Attribute;
+  rnd->type = CFXJSE_Engine::ResolveResult::Type::kAttribute;
+  rnd->script_attribute = info.value();
+  rnd->objects.emplace_back(curNode);
   return true;
 }
 
-bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) {
+bool CFXJSE_ResolveProcessor::ResolveNormal(v8::Isolate* pIsolate,
+                                            NodeData& rnd) {
   if (rnd.m_nLevel > 32 || !rnd.m_CurObject->IsNode())
     return false;
 
   CXFA_Node* curNode = rnd.m_CurObject->AsNode();
-  size_t nNum = rnd.m_Objects.size();
-  uint32_t dwStyles = rnd.m_dwStyles;
+  size_t nNum = rnd.m_Result.objects.size();
+  Mask<XFA_ResolveFlag> dwStyles = rnd.m_dwStyles;
   WideString& wsName = rnd.m_wsName;
   XFA_HashCode uNameHash = rnd.m_uHashName;
   WideString& wsCondition = rnd.m_wsCondition;
 
-  CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get());
+  NodeData rndFind;
   rndFind.m_wsName = rnd.m_wsName;
   rndFind.m_wsCondition = rnd.m_wsCondition;
   rndFind.m_nLevel = rnd.m_nLevel + 1;
@@ -280,36 +261,37 @@
     else
       children.push_back(pChild);
   }
-  if ((dwStyles & XFA_RESOLVENODE_Properties) && pVariablesNode) {
+  if ((dwStyles & XFA_ResolveFlag::kProperties) && pVariablesNode) {
     if (pVariablesNode->GetClassHashCode() == uNameHash) {
-      rnd.m_Objects.emplace_back(pVariablesNode);
+      rnd.m_Result.objects.emplace_back(pVariablesNode);
     } else {
       rndFind.m_CurObject = pVariablesNode;
       SetStylesForChild(dwStyles, rndFind);
       WideString wsSaveCondition = std::move(rndFind.m_wsCondition);
-      ResolveNormal(rndFind);
+      ResolveNormal(pIsolate, rndFind);
       rndFind.m_wsCondition = std::move(wsSaveCondition);
-      rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                           rndFind.m_Objects.end());
-      rndFind.m_Objects.clear();
+      rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                  rndFind.m_Result.objects.begin(),
+                                  rndFind.m_Result.objects.end());
+      rndFind.m_Result.objects.clear();
     }
-    if (rnd.m_Objects.size() > nNum) {
-      FilterCondition(wsCondition, &rnd);
-      return !rnd.m_Objects.empty();
+    if (rnd.m_Result.objects.size() > nNum) {
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      return !rnd.m_Result.objects.empty();
     }
   }
 
-  if (dwStyles & XFA_RESOLVENODE_Children) {
+  if (dwStyles & XFA_ResolveFlag::kChildren) {
     bool bSetFlag = false;
-    if (pPageSetNode && (dwStyles & XFA_RESOLVENODE_Properties))
+    if (pPageSetNode && (dwStyles & XFA_ResolveFlag::kProperties))
       children.push_back(pPageSetNode);
 
     for (CXFA_Node* child : children) {
-      if (dwStyles & XFA_RESOLVENODE_TagName) {
+      if (dwStyles & XFA_ResolveFlag::kTagName) {
         if (child->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.emplace_back(child);
+          rnd.m_Result.objects.emplace_back(child);
       } else if (child->GetNameHash() == uNameHash) {
-        rnd.m_Objects.emplace_back(child);
+        rnd.m_Result.objects.emplace_back(child);
       }
 
       if (child->GetElementType() != XFA_Element::PageSet &&
@@ -321,54 +303,55 @@
         rndFind.m_CurObject = child;
 
         WideString wsSaveCondition = std::move(rndFind.m_wsCondition);
-        ResolveNormal(rndFind);
+        ResolveNormal(pIsolate, rndFind);
         rndFind.m_wsCondition = std::move(wsSaveCondition);
-        rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                             rndFind.m_Objects.end());
-        rndFind.m_Objects.clear();
+        rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                    rndFind.m_Result.objects.begin(),
+                                    rndFind.m_Result.objects.end());
+        rndFind.m_Result.objects.clear();
       }
     }
-    if (rnd.m_Objects.size() > nNum) {
-      if (!(dwStyles & XFA_RESOLVENODE_ALL)) {
+    if (rnd.m_Result.objects.size() > nNum) {
+      if (!(dwStyles & XFA_ResolveFlag::kALL)) {
         std::vector<CXFA_Node*> upArrayNodes;
         if (curNode->IsTransparent()) {
-          CXFA_Node* pCurrent = ToNode(rnd.m_Objects.front().Get());
+          CXFA_Node* pCurrent = ToNode(rnd.m_Result.objects.front().Get());
           if (pCurrent) {
             upArrayNodes =
-                pCurrent->GetSiblings(!!(dwStyles & XFA_RESOLVENODE_TagName));
+                pCurrent->GetSiblings(!!(dwStyles & XFA_ResolveFlag::kTagName));
           }
         }
-        if (upArrayNodes.size() > rnd.m_Objects.size()) {
-          CXFA_Object* pSaveObject = rnd.m_Objects.front().Get();
-          rnd.m_Objects = std::vector<UnownedPtr<CXFA_Object>>(
+        if (upArrayNodes.size() > rnd.m_Result.objects.size()) {
+          CXFA_Object* pSaveObject = rnd.m_Result.objects.front().Get();
+          rnd.m_Result.objects = std::vector<cppgc::Member<CXFA_Object>>(
               upArrayNodes.begin(), upArrayNodes.end());
-          rnd.m_Objects.front() = pSaveObject;
+          rnd.m_Result.objects.front() = pSaveObject;
         }
       }
-      FilterCondition(wsCondition, &rnd);
-      return !rnd.m_Objects.empty();
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      return !rnd.m_Result.objects.empty();
     }
   }
-  if (dwStyles & XFA_RESOLVENODE_Attributes) {
-    if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView()))
-      return 1;
+  if (dwStyles & XFA_ResolveFlag::kAttributes) {
+    if (ResolveForAttributeRs(curNode, &rnd.m_Result, wsName.AsStringView()))
+      return true;
   }
-  if (dwStyles & XFA_RESOLVENODE_Properties) {
+  if (dwStyles & XFA_ResolveFlag::kProperties) {
     for (CXFA_Node* pChildProperty : properties) {
       if (pChildProperty->IsUnnamed()) {
         if (pChildProperty->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.emplace_back(pChildProperty);
+          rnd.m_Result.objects.emplace_back(pChildProperty);
         continue;
       }
       if (pChildProperty->GetNameHash() == uNameHash &&
           pChildProperty->GetElementType() != XFA_Element::Extras &&
           pChildProperty->GetElementType() != XFA_Element::Items) {
-        rnd.m_Objects.emplace_back(pChildProperty);
+        rnd.m_Result.objects.emplace_back(pChildProperty);
       }
     }
-    if (rnd.m_Objects.size() > nNum) {
-      FilterCondition(wsCondition, &rnd);
-      return !rnd.m_Objects.empty();
+    if (rnd.m_Result.objects.size() > nNum) {
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      return !rnd.m_Result.objects.empty();
     }
 
     CXFA_Node* pProp = nullptr;
@@ -388,8 +371,8 @@
       }
     }
     if (pProp) {
-      rnd.m_Objects.emplace_back(pProp);
-      return !rnd.m_Objects.empty();
+      rnd.m_Result.objects.emplace_back(pProp);
+      return !rnd.m_Result.objects.empty();
     }
   }
 
@@ -397,35 +380,35 @@
   uint32_t uCurClassHash = curNode->GetClassHashCode();
   if (!parentNode) {
     if (uCurClassHash == uNameHash) {
-      rnd.m_Objects.emplace_back(curNode);
-      FilterCondition(wsCondition, &rnd);
-      if (!rnd.m_Objects.empty())
+      rnd.m_Result.objects.emplace_back(curNode);
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      if (!rnd.m_Result.objects.empty())
         return true;
     }
     return false;
   }
 
-  if (dwStyles & XFA_RESOLVENODE_Siblings) {
+  if (dwStyles & XFA_ResolveFlag::kSiblings) {
     CXFA_Node* child = parentNode->GetFirstChild();
-    uint32_t dwSubStyles =
-        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties;
-    if (dwStyles & XFA_RESOLVENODE_TagName)
-      dwSubStyles |= XFA_RESOLVENODE_TagName;
-    if (dwStyles & XFA_RESOLVENODE_ALL)
-      dwSubStyles |= XFA_RESOLVENODE_ALL;
+    Mask<XFA_ResolveFlag> dwSubStyles = {XFA_ResolveFlag::kChildren,
+                                         XFA_ResolveFlag::kProperties};
+    if (dwStyles & XFA_ResolveFlag::kTagName)
+      dwSubStyles |= XFA_ResolveFlag::kTagName;
+    if (dwStyles & XFA_ResolveFlag::kALL)
+      dwSubStyles |= XFA_ResolveFlag::kALL;
 
     rndFind.m_dwStyles = dwSubStyles;
     while (child) {
       if (child == curNode) {
-        if (dwStyles & XFA_RESOLVENODE_TagName) {
+        if (dwStyles & XFA_ResolveFlag::kTagName) {
           if (uCurClassHash == uNameHash)
-            rnd.m_Objects.emplace_back(curNode);
+            rnd.m_Result.objects.emplace_back(curNode);
         } else {
           if (child->GetNameHash() == uNameHash) {
-            rnd.m_Objects.emplace_back(curNode);
+            rnd.m_Result.objects.emplace_back(curNode);
             if (rnd.m_nLevel == 0 && wsCondition.IsEmpty()) {
-              rnd.m_Objects.clear();
-              rnd.m_Objects.emplace_back(curNode);
+              rnd.m_Result.objects.clear();
+              rnd.m_Result.objects.emplace_back(curNode);
               return true;
             }
           }
@@ -434,11 +417,11 @@
         continue;
       }
 
-      if (dwStyles & XFA_RESOLVENODE_TagName) {
+      if (dwStyles & XFA_ResolveFlag::kTagName) {
         if (child->GetClassHashCode() == uNameHash)
-          rnd.m_Objects.emplace_back(child);
+          rnd.m_Result.objects.emplace_back(child);
       } else if (child->GetNameHash() == uNameHash) {
-        rnd.m_Objects.emplace_back(child);
+        rnd.m_Result.objects.emplace_back(child);
       }
 
       bool bInnerSearch = false;
@@ -453,70 +436,74 @@
       if (bInnerSearch) {
         rndFind.m_CurObject = child;
         WideString wsOriginCondition = std::move(rndFind.m_wsCondition);
-        uint32_t dwOriginStyle = rndFind.m_dwStyles;
-        rndFind.m_dwStyles = dwOriginStyle | XFA_RESOLVENODE_ALL;
-        ResolveNormal(rndFind);
+        Mask<XFA_ResolveFlag> dwOriginStyle = rndFind.m_dwStyles;
+        rndFind.m_dwStyles = dwOriginStyle | XFA_ResolveFlag::kALL;
+        ResolveNormal(pIsolate, rndFind);
         rndFind.m_dwStyles = dwOriginStyle;
         rndFind.m_wsCondition = std::move(wsOriginCondition);
-        rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                             rndFind.m_Objects.end());
-        rndFind.m_Objects.clear();
+        rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                    rndFind.m_Result.objects.begin(),
+                                    rndFind.m_Result.objects.end());
+        rndFind.m_Result.objects.clear();
       }
       child = child->GetNextSibling();
     }
-    if (rnd.m_Objects.size() > nNum) {
+    if (rnd.m_Result.objects.size() > nNum) {
       if (parentNode->IsTransparent()) {
         std::vector<CXFA_Node*> upArrayNodes;
-        CXFA_Node* pCurrent = ToNode(rnd.m_Objects.front().Get());
+        CXFA_Node* pCurrent = ToNode(rnd.m_Result.objects.front().Get());
         if (pCurrent) {
           upArrayNodes =
-              pCurrent->GetSiblings(!!(dwStyles & XFA_RESOLVENODE_TagName));
+              pCurrent->GetSiblings(!!(dwStyles & XFA_ResolveFlag::kTagName));
         }
-        if (upArrayNodes.size() > rnd.m_Objects.size()) {
-          CXFA_Object* pSaveObject = rnd.m_Objects.front().Get();
-          rnd.m_Objects = std::vector<UnownedPtr<CXFA_Object>>(
+        if (upArrayNodes.size() > rnd.m_Result.objects.size()) {
+          CXFA_Object* pSaveObject = rnd.m_Result.objects.front().Get();
+          rnd.m_Result.objects = std::vector<cppgc::Member<CXFA_Object>>(
               upArrayNodes.begin(), upArrayNodes.end());
-          rnd.m_Objects.front() = pSaveObject;
+          rnd.m_Result.objects.front() = pSaveObject;
         }
       }
-      FilterCondition(wsCondition, &rnd);
-      return !rnd.m_Objects.empty();
+      FilterCondition(pIsolate, wsCondition, &rnd);
+      return !rnd.m_Result.objects.empty();
     }
   }
 
-  if (dwStyles & XFA_RESOLVENODE_Parent) {
-    uint32_t dwSubStyles = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent |
-                           XFA_RESOLVENODE_Properties;
-    if (dwStyles & XFA_RESOLVENODE_TagName)
-      dwSubStyles |= XFA_RESOLVENODE_TagName;
-    if (dwStyles & XFA_RESOLVENODE_ALL)
-      dwSubStyles |= XFA_RESOLVENODE_ALL;
+  if (dwStyles & XFA_ResolveFlag::kParent) {
+    Mask<XFA_ResolveFlag> dwSubStyles = {XFA_ResolveFlag::kSiblings,
+                                         XFA_ResolveFlag::kParent,
+                                         XFA_ResolveFlag::kProperties};
+    if (dwStyles & XFA_ResolveFlag::kTagName)
+      dwSubStyles |= XFA_ResolveFlag::kTagName;
+    if (dwStyles & XFA_ResolveFlag::kALL)
+      dwSubStyles |= XFA_ResolveFlag::kALL;
 
+    m_pEngine->AddObjectToUpArray(parentNode);
     rndFind.m_dwStyles = dwSubStyles;
     rndFind.m_CurObject = parentNode;
-    rnd.m_pSC->GetUpObjectArray()->push_back(parentNode);
-    ResolveNormal(rndFind);
-    rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(),
-                         rndFind.m_Objects.end());
-    rndFind.m_Objects.clear();
-    if (rnd.m_Objects.size() > nNum)
+    ResolveNormal(pIsolate, rndFind);
+    rnd.m_Result.objects.insert(rnd.m_Result.objects.end(),
+                                rndFind.m_Result.objects.begin(),
+                                rndFind.m_Result.objects.end());
+    rndFind.m_Result.objects.clear();
+    if (rnd.m_Result.objects.size() > nNum)
       return true;
   }
   return false;
 }
 
-bool CFXJSE_ResolveProcessor::ResolveAsterisk(CFXJSE_ResolveNodeData& rnd) {
-  CXFA_Node* curNode = ToNode(rnd.m_CurObject.Get());
+bool CFXJSE_ResolveProcessor::ResolveAsterisk(NodeData& rnd) {
+  CXFA_Node* curNode = ToNode(rnd.m_CurObject);
   std::vector<CXFA_Node*> array = curNode->GetNodeListWithFilter(
-      XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties);
-  rnd.m_Objects.insert(rnd.m_Objects.end(), array.begin(), array.end());
-  return !rnd.m_Objects.empty();
+      {XFA_NodeFilter::kChildren, XFA_NodeFilter::kProperties});
+  rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), array.begin(),
+                              array.end());
+  return !rnd.m_Result.objects.empty();
 }
 
 int32_t CFXJSE_ResolveProcessor::GetFilter(WideStringView wsExpression,
                                            int32_t nStart,
-                                           CFXJSE_ResolveNodeData& rnd) {
-  ASSERT(nStart > -1);
+                                           NodeData& rnd) {
+  DCHECK(nStart > -1);
 
   int32_t iLength = wsExpression.GetLength();
   if (nStart >= iLength)
@@ -541,7 +528,7 @@
       wCur = pSrc[nStart++];
       if (wCur == '.') {
         if (nNameCount == 0) {
-          rnd.m_dwStyles |= XFA_RESOLVENODE_AnyChild;
+          rnd.m_dwStyles |= XFA_ResolveFlag::kAnyChild;
           continue;
         }
         if (wPrev == '\\') {
@@ -589,14 +576,14 @@
   wsName.Trim();
   wsCondition.Trim();
   rnd.m_uHashName =
-      static_cast<XFA_HashCode>(FX_HashCode_GetW(wsName.AsStringView(), false));
+      static_cast<XFA_HashCode>(FX_HashCode_GetW(wsName.AsStringView()));
   return nStart;
 }
 
 void CFXJSE_ResolveProcessor::ConditionArray(size_t iCurIndex,
                                              WideString wsCondition,
                                              size_t iFoundCount,
-                                             CFXJSE_ResolveNodeData* pRnd) {
+                                             NodeData* pRnd) {
   size_t iLen = wsCondition.GetLength();
   bool bRelative = false;
   bool bAll = false;
@@ -613,18 +600,18 @@
     break;
   }
   if (bAll) {
-    if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-      if (pRnd->m_dwStyles & XFA_RESOLVENODE_Bind) {
-        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+    if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) {
+      if (pRnd->m_dwStyles & XFA_ResolveFlag::kBind) {
+        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject);
         m_pNodeHelper->m_iCreateCount = 1;
-        pRnd->m_Objects.clear();
+        pRnd->m_Result.objects.clear();
         m_pNodeHelper->m_iCurAllStart = -1;
         m_pNodeHelper->m_pAllStartParent = nullptr;
       } else if (m_pNodeHelper->m_iCurAllStart == -1) {
         m_pNodeHelper->m_iCurAllStart = m_iCurStart;
-        m_pNodeHelper->m_pAllStartParent = ToNode(pRnd->m_CurObject.Get());
+        m_pNodeHelper->m_pAllStartParent = ToNode(pRnd->m_CurObject);
       }
-    } else if (pRnd->m_dwStyles & XFA_RESOLVENODE_BindNew) {
+    } else if (pRnd->m_dwStyles & XFA_ResolveFlag::kBindNew) {
       if (m_pNodeHelper->m_iCurAllStart == -1)
         m_pNodeHelper->m_iCurAllStart = m_iCurStart;
     }
@@ -638,51 +625,51 @@
     iIndex += iCurIndex;
 
   if (iIndex < 0 || static_cast<size_t>(iIndex) >= iFoundCount) {
-    if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-      m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+    if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) {
+      m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject);
       m_pNodeHelper->m_iCreateCount = iIndex - iFoundCount + 1;
     }
-    pRnd->m_Objects.clear();
+    pRnd->m_Result.objects.clear();
   } else {
-    pRnd->m_Objects =
-        std::vector<UnownedPtr<CXFA_Object>>(1, pRnd->m_Objects[iIndex]);
+    pRnd->m_Result.objects = std::vector<cppgc::Member<CXFA_Object>>(
+        1, pRnd->m_Result.objects[iIndex]);
   }
 }
 
-void CFXJSE_ResolveProcessor::FilterCondition(WideString wsCondition,
-                                              CFXJSE_ResolveNodeData* pRnd) {
+void CFXJSE_ResolveProcessor::FilterCondition(v8::Isolate* pIsolate,
+                                              WideString wsCondition,
+                                              NodeData* pRnd) {
   size_t iCurIndex = 0;
-  const std::vector<CXFA_Node*>* pArray = pRnd->m_pSC->GetUpObjectArray();
-  if (!pArray->empty()) {
-    CXFA_Node* pNode = pArray->back();
-    bool bIsProperty = pNode->IsProperty();
-    bool bIsClassIndex =
+  CXFA_Node* pNode = m_pEngine->LastObjectFromUpArray();
+  if (pNode) {
+    const bool bIsProperty = pNode->IsProperty();
+    const bool bIsClassIndex =
         pNode->IsUnnamed() ||
         (bIsProperty && pNode->GetElementType() != XFA_Element::PageSet);
     iCurIndex = pNode->GetIndex(bIsProperty, bIsClassIndex);
   }
 
-  size_t iFoundCount = pRnd->m_Objects.size();
+  size_t iFoundCount = pRnd->m_Result.objects.size();
   wsCondition.Trim();
 
-  int32_t iLen = wsCondition.GetLength();
-  if (!iLen) {
-    if (pRnd->m_dwStyles & XFA_RESOLVENODE_ALL)
+  const size_t nLen = wsCondition.GetLength();
+  if (nLen == 0) {
+    if (pRnd->m_dwStyles & XFA_ResolveFlag::kALL)
       return;
     if (iFoundCount == 1)
       return;
 
     if (iFoundCount <= iCurIndex) {
-      if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) {
-        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get());
+      if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) {
+        m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject);
         m_pNodeHelper->m_iCreateCount = iCurIndex - iFoundCount + 1;
       }
-      pRnd->m_Objects.clear();
+      pRnd->m_Result.objects.clear();
       return;
     }
 
-    pRnd->m_Objects =
-        std::vector<UnownedPtr<CXFA_Object>>(1, pRnd->m_Objects[iCurIndex]);
+    pRnd->m_Result.objects = std::vector<cppgc::Member<CXFA_Object>>(
+        1, pRnd->m_Result.objects[iCurIndex]);
     return;
   }
 
@@ -692,8 +679,8 @@
       ConditionArray(iCurIndex, wsCondition, iFoundCount, pRnd);
       return;
     case '.':
-      if (iLen > 1 && (wsCondition[1] == '[' || wsCondition[1] == '('))
-        DoPredicateFilter(wsCondition, iFoundCount, pRnd);
+      if (nLen > 1 && (wsCondition[1] == '[' || wsCondition[1] == '('))
+        DoPredicateFilter(pIsolate, wsCondition, iFoundCount, pRnd);
       return;
     case '(':
     case '"':
@@ -702,34 +689,50 @@
   }
 }
 
-void CFXJSE_ResolveProcessor::SetStylesForChild(uint32_t dwParentStyles,
-                                                CFXJSE_ResolveNodeData& rnd) {
-  uint32_t dwSubStyles = XFA_RESOLVENODE_Children;
-  if (dwParentStyles & XFA_RESOLVENODE_TagName)
-    dwSubStyles |= XFA_RESOLVENODE_TagName;
-
-  dwSubStyles &= ~XFA_RESOLVENODE_Parent;
-  dwSubStyles &= ~XFA_RESOLVENODE_Siblings;
-  dwSubStyles &= ~XFA_RESOLVENODE_Properties;
-  dwSubStyles |= XFA_RESOLVENODE_ALL;
+void CFXJSE_ResolveProcessor::SetStylesForChild(
+    Mask<XFA_ResolveFlag> dwParentStyles,
+    NodeData& rnd) {
+  Mask<XFA_ResolveFlag> dwSubStyles = {XFA_ResolveFlag::kChildren,
+                                       XFA_ResolveFlag::kALL};
+  if (dwParentStyles & XFA_ResolveFlag::kTagName)
+    dwSubStyles |= XFA_ResolveFlag::kTagName;
   rnd.m_dwStyles = dwSubStyles;
 }
 
-void CFXJSE_ResolveProcessor::SetIndexDataBind(WideString& wsNextCondition,
-                                               int32_t& iIndex,
-                                               int32_t iCount) {
-  if (m_pNodeHelper->CreateNodeForCondition(wsNextCondition)) {
-    if (m_pNodeHelper->m_eLastCreateType == XFA_Element::DataGroup) {
-      iIndex = 0;
-    } else {
-      iIndex = iCount - 1;
-    }
-  } else {
-    iIndex = iCount - 1;
+int32_t CFXJSE_ResolveProcessor::IndexForDataBind(
+    const WideString& wsNextCondition,
+    int32_t iCount) {
+  if (m_pNodeHelper->CreateNodeForCondition(wsNextCondition) &&
+      m_pNodeHelper->m_eLastCreateType == XFA_Element::DataGroup) {
+    return 0;
+  }
+  return iCount - 1;
+}
+
+void CFXJSE_ResolveProcessor::DoPredicateFilter(v8::Isolate* pIsolate,
+                                                WideString wsCondition,
+                                                size_t iFoundCount,
+                                                NodeData* pRnd) {
+  DCHECK_EQ(iFoundCount, pRnd->m_Result.objects.size());
+  CXFA_Script::Type eLangType = CXFA_Script::Type::Unknown;
+  if (wsCondition.First(2).EqualsASCII(".[") && wsCondition.Back() == L']')
+    eLangType = CXFA_Script::Type::Formcalc;
+  else if (wsCondition.First(2).EqualsASCII(".(") && wsCondition.Back() == L')')
+    eLangType = CXFA_Script::Type::Javascript;
+  else
+    return;
+
+  WideString wsExpression = wsCondition.Substr(2, wsCondition.GetLength() - 3);
+  for (size_t i = iFoundCount; i > 0; --i) {
+    auto pRetValue = std::make_unique<CFXJSE_Value>();
+    bool bRet = m_pEngine->RunScript(eLangType, wsExpression.AsStringView(),
+                                     pRetValue.get(),
+                                     pRnd->m_Result.objects[i - 1].Get());
+    if (!bRet || !pRetValue->ToBoolean(pIsolate))
+      pRnd->m_Result.objects.erase(pRnd->m_Result.objects.begin() + i - 1);
   }
 }
 
-CFXJSE_ResolveNodeData::CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC)
-    : m_pSC(pSC) {}
+CFXJSE_ResolveProcessor::NodeData::NodeData() = default;
 
-CFXJSE_ResolveNodeData::~CFXJSE_ResolveNodeData() = default;
+CFXJSE_ResolveProcessor::NodeData::~NodeData() = default;
diff --git a/fxjs/xfa/cfxjse_resolveprocessor.h b/fxjs/xfa/cfxjse_resolveprocessor.h
index 6e4fcba..f7125c9 100644
--- a/fxjs/xfa/cfxjse_resolveprocessor.h
+++ b/fxjs/xfa/cfxjse_resolveprocessor.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,69 +8,69 @@
 #define FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_
 
 #include <memory>
-#include <vector>
 
-#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "v8/include/cppgc/macros.h"
 #include "xfa/fxfa/fxfa_basic.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
-class CXFA_NodeHelper;
-
-class CFXJSE_ResolveNodeData {
- public:
-  explicit CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC);
-  ~CFXJSE_ResolveNodeData();
-
-  UnownedPtr<CFXJSE_Engine> const m_pSC;
-  UnownedPtr<CXFA_Object> m_CurObject;
-  WideString m_wsName;
-  WideString m_wsCondition;
-  XFA_HashCode m_uHashName = XFA_HASHCODE_None;
-  int32_t m_nLevel = 0;
-  uint32_t m_dwStyles = XFA_RESOLVENODE_Children;
-  XFA_ResolveNode_RSType m_dwFlag = XFA_ResolveNode_RSType_Nodes;
-  std::vector<UnownedPtr<CXFA_Object>> m_Objects;
-  XFA_SCRIPTATTRIBUTEINFO m_ScriptAttribute;
-};
+class CFXJSE_NodeHelper;
 
 class CFXJSE_ResolveProcessor {
  public:
-  CFXJSE_ResolveProcessor();
+  class NodeData {
+    CPPGC_STACK_ALLOCATED();  // Allows Raw/Unowned pointers.
+
+   public:
+    NodeData();
+    ~NodeData();
+
+    UnownedPtr<CXFA_Object> m_CurObject;  // Ok, stack-only.
+    WideString m_wsName;
+    WideString m_wsCondition;
+    XFA_HashCode m_uHashName = XFA_HASHCODE_None;
+    int32_t m_nLevel = 0;
+    Mask<XFA_ResolveFlag> m_dwStyles = XFA_ResolveFlag::kChildren;
+    CFXJSE_Engine::ResolveResult m_Result;
+  };
+
+  CFXJSE_ResolveProcessor(CFXJSE_Engine* pEngine, CFXJSE_NodeHelper* pHelper);
   ~CFXJSE_ResolveProcessor();
 
-  bool Resolve(CFXJSE_ResolveNodeData& rnd);
-  int32_t GetFilter(WideStringView wsExpression,
-                    int32_t nStart,
-                    CFXJSE_ResolveNodeData& rnd);
-  void SetIndexDataBind(WideString& wsNextCondition,
-                        int32_t& iIndex,
-                        int32_t iCount);
+  bool Resolve(v8::Isolate* pIsolate, NodeData& rnd);
+  int32_t GetFilter(WideStringView wsExpression, int32_t nStart, NodeData& rnd);
+  int32_t IndexForDataBind(const WideString& wsNextCondition, int32_t iCount);
   void SetCurStart(int32_t start) { m_iCurStart = start; }
 
-  CXFA_NodeHelper* GetNodeHelper() { return m_pNodeHelper.get(); }
-
  private:
   bool ResolveForAttributeRs(CXFA_Object* curNode,
-                             CFXJSE_ResolveNodeData& rnd,
+                             CFXJSE_Engine::ResolveResult* rnd,
                              WideStringView strAttr);
-  bool ResolveAnyChild(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveDollar(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveNumberSign(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveAsterisk(CFXJSE_ResolveNodeData& rnd);
-  bool ResolveNormal(CFXJSE_ResolveNodeData& rnd);
-  void SetStylesForChild(uint32_t dwParentStyles, CFXJSE_ResolveNodeData& rnd);
+  bool ResolveAnyChild(v8::Isolate* pIsolate, NodeData& rnd);
+  bool ResolveDollar(v8::Isolate* pIsolate, NodeData& rnd);
+  bool ResolveExcalmatory(v8::Isolate* pIsolate, NodeData& rnd);
+  bool ResolveNumberSign(v8::Isolate* pIsolate, NodeData& rnd);
+  bool ResolveAsterisk(NodeData& rnd);
+  bool ResolveNormal(v8::Isolate* pIsolate, NodeData& rnd);
+  void SetStylesForChild(Mask<XFA_ResolveFlag> dwParentStyles, NodeData& rnd);
 
   void ConditionArray(size_t iCurIndex,
                       WideString wsCondition,
                       size_t iFoundCount,
-                      CFXJSE_ResolveNodeData* pRnd);
-  void FilterCondition(WideString wsCondition, CFXJSE_ResolveNodeData* pRnd);
+                      NodeData* pRnd);
+  void FilterCondition(v8::Isolate* pIsolate,
+                       WideString wsCondition,
+                       NodeData* pRnd);
+  void DoPredicateFilter(v8::Isolate* pIsolate,
+                         WideString wsCondition,
+                         size_t iFoundCount,
+                         NodeData* pRnd);
 
   int32_t m_iCurStart = 0;
-  std::unique_ptr<CXFA_NodeHelper> const m_pNodeHelper;
+  UnownedPtr<CFXJSE_Engine> const m_pEngine;
+  UnownedPtr<CFXJSE_NodeHelper> const m_pNodeHelper;
 };
 
 #endif  // FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_
diff --git a/fxjs/xfa/cfxjse_runtimedata.cpp b/fxjs/xfa/cfxjse_runtimedata.cpp
index 0478e3e..b6a24c4 100644
--- a/fxjs/xfa/cfxjse_runtimedata.cpp
+++ b/fxjs/xfa/cfxjse_runtimedata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,15 @@
 #include <utility>
 
 #include "fxjs/cfxjs_engine.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-external.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-template.h"
 
 CFXJSE_RuntimeData::CFXJSE_RuntimeData() = default;
 
@@ -19,25 +27,21 @@
     v8::Isolate* pIsolate) {
   std::unique_ptr<CFXJSE_RuntimeData> pRuntimeData(new CFXJSE_RuntimeData());
   CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
-
   v8::Local<v8::FunctionTemplate> hFuncTemplate =
       v8::FunctionTemplate::New(pIsolate);
 
   v8::Local<v8::ObjectTemplate> hGlobalTemplate =
       hFuncTemplate->InstanceTemplate();
-  hGlobalTemplate->Set(
-      v8::Symbol::GetToStringTag(pIsolate),
-      v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
-          .ToLocalChecked());
+  hGlobalTemplate->Set(v8::Symbol::GetToStringTag(pIsolate),
+                       fxv8::NewStringHelper(pIsolate, "global"));
 
   v8::Local<v8::Context> hContext =
       v8::Context::New(pIsolate, 0, hGlobalTemplate);
 
-  ASSERT(hContext->Global()->InternalFieldCount() == 0);
-  ASSERT(hContext->Global()
-             ->GetPrototype()
-             .As<v8::Object>()
-             ->InternalFieldCount() == 0);
+  DCHECK_EQ(hContext->Global()->InternalFieldCount(), 0);
+  DCHECK_EQ(
+      hContext->Global()->GetPrototype().As<v8::Object>()->InternalFieldCount(),
+      0);
 
   hContext->SetSecurityToken(v8::External::New(pIsolate, pIsolate));
   pRuntimeData->m_hRootContextGlobalTemplate.Reset(pIsolate, hFuncTemplate);
@@ -47,9 +51,8 @@
 
 CFXJSE_RuntimeData* CFXJSE_RuntimeData::Get(v8::Isolate* pIsolate) {
   FXJS_PerIsolateData::SetUp(pIsolate);
-
   FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(pIsolate);
-  if (!pData->m_pFXJSERuntimeData)
-    pData->m_pFXJSERuntimeData = CFXJSE_RuntimeData::Create(pIsolate);
-  return static_cast<CFXJSE_RuntimeData*>(pData->m_pFXJSERuntimeData.get());
+  if (!pData->GetExtension())
+    pData->SetExtension(CFXJSE_RuntimeData::Create(pIsolate));
+  return static_cast<CFXJSE_RuntimeData*>(pData->GetExtension());
 }
diff --git a/fxjs/xfa/cfxjse_runtimedata.h b/fxjs/xfa/cfxjse_runtimedata.h
index 148b01e..67925a0 100644
--- a/fxjs/xfa/cfxjse_runtimedata.h
+++ b/fxjs/xfa/cfxjse_runtimedata.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,25 +10,26 @@
 #include <memory>
 
 #include "fxjs/cfxjs_engine.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
-class CFXJSE_RuntimeData : public FXJS_PerIsolateData::ExtensionIface {
+class CFXJSE_RuntimeData final : public FXJS_PerIsolateData::ExtensionIface {
  public:
+  CFXJSE_RuntimeData(const CFXJSE_RuntimeData&) = delete;
+  CFXJSE_RuntimeData& operator=(const CFXJSE_RuntimeData&) = delete;
   ~CFXJSE_RuntimeData() override;
 
   static CFXJSE_RuntimeData* Get(v8::Isolate* pIsolate);
 
-  v8::Global<v8::FunctionTemplate> m_hRootContextGlobalTemplate;
-  v8::Global<v8::Context> m_hRootContext;
-
- protected:
-  CFXJSE_RuntimeData();
-
-  static std::unique_ptr<CFXJSE_RuntimeData> Create(v8::Isolate* pIsolate);
+  const v8::Global<v8::Context>& GetRootContext() { return m_hRootContext; }
 
  private:
-  CFXJSE_RuntimeData(const CFXJSE_RuntimeData&) = delete;
-  CFXJSE_RuntimeData& operator=(const CFXJSE_RuntimeData&) = delete;
+  static std::unique_ptr<CFXJSE_RuntimeData> Create(v8::Isolate* pIsolate);
+
+  CFXJSE_RuntimeData();
+
+  v8::Global<v8::FunctionTemplate> m_hRootContextGlobalTemplate;
+  v8::Global<v8::Context> m_hRootContext;
 };
 
 #endif  // FXJS_XFA_CFXJSE_RUNTIMEDATA_H_
diff --git a/fxjs/xfa/cfxjse_value.cpp b/fxjs/xfa/cfxjse_value.cpp
index 1d4cec4..112a648 100644
--- a/fxjs/xfa/cfxjse_value.cpp
+++ b/fxjs/xfa/cfxjse_value.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,16 @@
 
 #include <math.h>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_context.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-exception.h"
+#include "v8/include/v8-function.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-script.h"
 
 namespace {
 
@@ -26,15 +33,18 @@
   if (nErrExp >= 0)
     return fNumber;
 
-  double dwError = pow(2.0, nErrExp), dwErrorHalf = dwError / 2;
-  double dNumber = fNumber, dNumberAbs = fabs(fNumber);
-  double dNumberAbsMin = dNumberAbs - dwErrorHalf,
-         dNumberAbsMax = dNumberAbs + dwErrorHalf;
+  double dwError = pow(2.0, nErrExp);
+  double dwErrorHalf = dwError / 2;
+  double dNumber = fNumber;
+  double dNumberAbs = fabs(fNumber);
+  double dNumberAbsMin = dNumberAbs - dwErrorHalf;
+  double dNumberAbsMax = dNumberAbs + dwErrorHalf;
   int32_t iErrPos = 0;
   if (floor(dNumberAbsMin) == floor(dNumberAbsMax)) {
     dNumberAbsMin = fmod(dNumberAbsMin, 1.0);
     dNumberAbsMax = fmod(dNumberAbsMax, 1.0);
-    int32_t iErrPosMin = 1, iErrPosMax = 38;
+    int32_t iErrPosMin = 1;
+    int32_t iErrPosMax = 38;
     do {
       int32_t iMid = (iErrPosMin + iErrPosMax) / 2;
       double dPow = pow(10.0, iMid);
@@ -53,228 +63,140 @@
 
 }  // namespace
 
-void FXJSE_ThrowMessage(ByteStringView utf8Message) {
-  v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
-  ASSERT(pIsolate);
-
+void FXJSE_ThrowMessage(v8::Isolate* pIsolate, ByteStringView utf8Message) {
+  DCHECK(pIsolate);
   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
-  v8::Local<v8::String> hMessage =
-      v8::String::NewFromUtf8(pIsolate, utf8Message.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              utf8Message.GetLength())
-          .ToLocalChecked();
+  v8::Local<v8::String> hMessage = fxv8::NewStringHelper(pIsolate, utf8Message);
   v8::Local<v8::Value> hError = v8::Exception::Error(hMessage);
   pIsolate->ThrowException(hError);
 }
 
-CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {}
+CFXJSE_Value::CFXJSE_Value() = default;
 
-CFXJSE_Value::~CFXJSE_Value() {}
-
-CFXJSE_HostObject* CFXJSE_Value::ToHostObject() const {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> pValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  ASSERT(!pValue.IsEmpty());
-  if (!pValue->IsObject())
-    return nullptr;
-
-  return FXJSE_RetrieveObjectBinding(pValue.As<v8::Object>());
+CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate, v8::Local<v8::Value> value) {
+  ForceSetValue(pIsolate, value);
 }
 
-void CFXJSE_Value::SetHostObject(CFXJSE_HostObject* lpObject,
+CFXJSE_Value::~CFXJSE_Value() = default;
+
+CFXJSE_HostObject* CFXJSE_Value::ToHostObject(v8::Isolate* pIsolate) const {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return CFXJSE_HostObject::FromV8(
+      v8::Local<v8::Value>::New(pIsolate, m_hValue));
+}
+
+void CFXJSE_Value::SetHostObject(v8::Isolate* pIsolate,
+                                 CFXJSE_HostObject* pObject,
                                  CFXJSE_Class* pClass) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::FunctionTemplate> hClass =
-      v8::Local<v8::FunctionTemplate>::New(GetIsolate(), pClass->m_hTemplate);
-  v8::Local<v8::Object> hObject =
-      hClass->InstanceTemplate()
-          ->NewInstance(GetIsolate()->GetCurrentContext())
-          .ToLocalChecked();
-  FXJSE_UpdateObjectBinding(hObject, lpObject);
-  m_hValue.Reset(GetIsolate(), hObject);
-}
-
-void CFXJSE_Value::ClearHostObject() {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  FXJSE_ClearObjectBinding(m_hValue.Get(GetIsolate()).As<v8::Object>());
-  v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
-  m_hValue.Reset(GetIsolate(), hValue);
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  m_hValue.Reset(pIsolate, pObject->NewBoundV8Object(
+                               pIsolate, pClass->GetTemplate(pIsolate)));
 }
 
 void CFXJSE_Value::SetArray(
+    v8::Isolate* pIsolate,
     const std::vector<std::unique_ptr<CFXJSE_Value>>& values) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Array> hArrayObject =
-      v8::Array::New(GetIsolate(), values.size());
-  v8::Local<v8::Context> context = GetIsolate()->GetCurrentContext();
-  uint32_t count = 0;
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  std::vector<v8::Local<v8::Value>> local_values;
+  local_values.reserve(values.size());
   for (auto& v : values) {
     if (v->IsEmpty())
-      v->SetUndefined();
-    hArrayObject
-        ->Set(
-            context, count++,
-            v8::Local<v8::Value>::New(GetIsolate(), v.get()->DirectGetValue()))
-        .FromJust();
+      local_values.push_back(fxv8::NewUndefinedHelper(pIsolate));
+    else
+      local_values.push_back(v->GetValue(pIsolate));
   }
-  m_hValue.Reset(GetIsolate(), hArrayObject);
+  v8::Local<v8::Array> hArrayObject =
+      v8::Array::New(pIsolate, local_values.data(), local_values.size());
+  m_hValue.Reset(pIsolate, hArrayObject);
 }
 
-void CFXJSE_Value::SetFloat(float fFloat) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> pValue = v8::Number::New(GetIsolate(), ftod(fFloat));
-  m_hValue.Reset(GetIsolate(), pValue);
+void CFXJSE_Value::SetFloat(v8::Isolate* pIsolate, float fFloat) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, ftod(fFloat)));
 }
 
-bool CFXJSE_Value::SetObjectProperty(ByteStringView szPropName,
-                                     CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  if (lpPropValue->IsEmpty())
+bool CFXJSE_Value::SetObjectProperty(v8::Isolate* pIsolate,
+                                     ByteStringView szPropName,
+                                     CFXJSE_Value* pPropValue) {
+  if (pPropValue->IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = GetValue(pIsolate);
   if (!hObject->IsObject())
     return false;
 
-  v8::Local<v8::String> hPropName =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
-  v8::Local<v8::Value> hPropValue =
-      v8::Local<v8::Value>::New(GetIsolate(), lpPropValue->DirectGetValue());
-  v8::Maybe<bool> result = hObject.As<v8::Object>()->Set(
-      GetIsolate()->GetCurrentContext(), hPropName, hPropValue);
-  return result.IsJust() && result.FromJust();
+  return fxv8::ReentrantPutObjectPropertyHelper(
+      pIsolate, hObject.As<v8::Object>(), szPropName,
+      pPropValue->GetValue(pIsolate));
 }
 
-bool CFXJSE_Value::GetObjectProperty(ByteStringView szPropName,
-                                     CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+bool CFXJSE_Value::GetObjectProperty(v8::Isolate* pIsolate,
+                                     ByteStringView szPropName,
+                                     CFXJSE_Value* pPropValue) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = GetValue(pIsolate);
   if (!hObject->IsObject())
     return false;
 
-  v8::Local<v8::String> hPropName =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
-  v8::Local<v8::Value> hPropValue =
-      hObject.As<v8::Object>()
-          ->Get(GetIsolate()->GetCurrentContext(), hPropName)
-          .ToLocalChecked();
-  lpPropValue->ForceSetValue(hPropValue);
+  pPropValue->ForceSetValue(
+      pIsolate, fxv8::ReentrantGetObjectPropertyHelper(
+                    pIsolate, hObject.As<v8::Object>(), szPropName));
   return true;
 }
 
-bool CFXJSE_Value::GetObjectPropertyByIdx(uint32_t uPropIdx,
-                                          CFXJSE_Value* lpPropValue) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  if (!hObject->IsObject())
+bool CFXJSE_Value::GetObjectPropertyByIdx(v8::Isolate* pIsolate,
+                                          uint32_t uPropIdx,
+                                          CFXJSE_Value* pPropValue) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = GetValue(pIsolate);
+  if (!hObject->IsArray())
     return false;
 
-  v8::Local<v8::Value> hPropValue =
-      hObject.As<v8::Object>()
-          ->Get(GetIsolate()->GetCurrentContext(), uPropIdx)
-          .ToLocalChecked();
-  lpPropValue->ForceSetValue(hPropValue);
+  pPropValue->ForceSetValue(pIsolate,
+                            fxv8::ReentrantGetArrayElementHelper(
+                                pIsolate, hObject.As<v8::Array>(), uPropIdx));
   return true;
 }
 
-bool CFXJSE_Value::DeleteObjectProperty(ByteStringView szPropName) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::String> hPropName =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
-  return hObject.As<v8::Object>()
-      ->Delete(GetIsolate()->GetCurrentContext(), hPropName)
-      .FromJust();
+void CFXJSE_Value::DeleteObjectProperty(v8::Isolate* pIsolate,
+                                        ByteStringView szPropName) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = v8::Local<v8::Value>::New(pIsolate, m_hValue);
+  if (hObject->IsObject()) {
+    fxv8::ReentrantDeleteObjectPropertyHelper(
+        pIsolate, hObject.As<v8::Object>(), szPropName);
+  }
 }
 
-bool CFXJSE_Value::HasObjectOwnProperty(ByteStringView szPropName,
-                                        bool bUseTypeGetter) {
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+bool CFXJSE_Value::SetObjectOwnProperty(v8::Isolate* pIsolate,
+                                        ByteStringView szPropName,
+                                        CFXJSE_Value* pPropValue) {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  v8::Local<v8::Value> hObject = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   if (!hObject->IsObject())
     return false;
 
-  v8::Local<v8::String> hKey =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
-  return hObject.As<v8::Object>()
-             ->HasRealNamedProperty(GetIsolate()->GetCurrentContext(), hKey)
-             .FromJust() ||
-         (bUseTypeGetter &&
-          hObject.As<v8::Object>()
-              ->HasOwnProperty(GetIsolate()->GetCurrentContext(), hKey)
-              .FromMaybe(false));
-}
-
-bool CFXJSE_Value::SetObjectOwnProperty(ByteStringView szPropName,
-                                        CFXJSE_Value* lpPropValue) {
-  ASSERT(lpPropValue);
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hObject =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  if (!hObject->IsObject())
-    return false;
-
-  v8::Local<v8::String> hPropName =
-      v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(),
-                              v8::NewStringType::kNormal,
-                              szPropName.GetLength())
-          .ToLocalChecked();
   v8::Local<v8::Value> pValue =
-      v8::Local<v8::Value>::New(GetIsolate(), lpPropValue->m_hValue);
-  return hObject.As<v8::Object>()
-      ->DefineOwnProperty(GetIsolate()->GetCurrentContext(), hPropName, pValue)
-      .FromMaybe(false);
+      v8::Local<v8::Value>::New(pIsolate, pPropValue->m_hValue);
+  return fxv8::ReentrantSetObjectOwnPropertyHelper(
+      pIsolate, hObject.As<v8::Object>(), szPropName, pValue);
 }
 
-bool CFXJSE_Value::SetFunctionBind(CFXJSE_Value* lpOldFunction,
-                                   CFXJSE_Value* lpNewThis) {
-  ASSERT(lpOldFunction);
-  ASSERT(lpNewThis);
+v8::Local<v8::Function> CFXJSE_Value::NewBoundFunction(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::Function> hOldFunction,
+    v8::Local<v8::Object> hNewThis) {
+  DCHECK(!hOldFunction.IsEmpty());
+  DCHECK(!hNewThis.IsEmpty());
 
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  CFXJSE_ScopeUtil_RootContext scope(pIsolate);
   v8::Local<v8::Value> rgArgs[2];
-  v8::Local<v8::Value> hOldFunction =
-      v8::Local<v8::Value>::New(GetIsolate(), lpOldFunction->DirectGetValue());
-  if (hOldFunction.IsEmpty() || !hOldFunction->IsFunction())
-    return false;
-
   rgArgs[0] = hOldFunction;
-  v8::Local<v8::Value> hNewThis =
-      v8::Local<v8::Value>::New(GetIsolate(), lpNewThis->DirectGetValue());
-  if (hNewThis.IsEmpty())
-    return false;
-
   rgArgs[1] = hNewThis;
-  v8::Local<v8::String> hBinderFuncSource =
-      v8::String::NewFromUtf8(GetIsolate(),
-                              "(function (oldfunction, newthis) { return "
-                              "oldfunction.bind(newthis); })",
-                              v8::NewStringType::kNormal)
-          .ToLocalChecked();
-  v8::Local<v8::Context> hContext = GetIsolate()->GetCurrentContext();
+  v8::Local<v8::String> hBinderFuncSource = fxv8::NewStringHelper(
+      pIsolate, "(function (fn, obj) { return fn.bind(obj); })");
+  v8::Local<v8::Context> hContext = pIsolate->GetCurrentContext();
   v8::Local<v8::Function> hBinderFunc =
       v8::Script::Compile(hContext, hBinderFuncSource)
           .ToLocalChecked()
@@ -284,187 +206,159 @@
   v8::Local<v8::Value> hBoundFunction =
       hBinderFunc->Call(hContext, hContext->Global(), 2, rgArgs)
           .ToLocalChecked();
-  if (hBoundFunction.IsEmpty() || !hBoundFunction->IsFunction())
-    return false;
+  if (!fxv8::IsFunction(hBoundFunction))
+    return v8::Local<v8::Function>();
 
-  m_hValue.Reset(GetIsolate(), hBoundFunction);
-  return true;
+  return hBoundFunction.As<v8::Function>();
+}
+
+v8::Local<v8::Value> CFXJSE_Value::GetValue(v8::Isolate* pIsolate) const {
+  return v8::Local<v8::Value>::New(pIsolate, m_hValue);
 }
 
 bool CFXJSE_Value::IsEmpty() const {
   return m_hValue.IsEmpty();
 }
 
-bool CFXJSE_Value::IsUndefined() const {
+bool CFXJSE_Value::IsUndefined(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsUndefined();
 }
 
-bool CFXJSE_Value::IsNull() const {
+bool CFXJSE_Value::IsNull(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsNull();
 }
 
-bool CFXJSE_Value::IsBoolean() const {
+bool CFXJSE_Value::IsBoolean(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsBoolean();
 }
 
-bool CFXJSE_Value::IsString() const {
+bool CFXJSE_Value::IsString(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsString();
 }
 
-bool CFXJSE_Value::IsNumber() const {
+bool CFXJSE_Value::IsNumber(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsNumber();
 }
 
-bool CFXJSE_Value::IsInteger() const {
+bool CFXJSE_Value::IsInteger(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsInt32();
 }
 
-bool CFXJSE_Value::IsObject() const {
+bool CFXJSE_Value::IsObject(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsObject();
 }
 
-bool CFXJSE_Value::IsArray() const {
+bool CFXJSE_Value::IsArray(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsArray();
 }
 
-bool CFXJSE_Value::IsFunction() const {
+bool CFXJSE_Value::IsFunction(v8::Isolate* pIsolate) const {
   if (IsEmpty())
     return false;
 
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  v8::Local<v8::Value> hValue = v8::Local<v8::Value>::New(pIsolate, m_hValue);
   return hValue->IsFunction();
 }
 
-bool CFXJSE_Value::ToBoolean() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  return hValue->BooleanValue(GetIsolate());
+bool CFXJSE_Value::ToBoolean(v8::Isolate* pIsolate) const {
+  DCHECK(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return fxv8::ReentrantToBooleanHelper(
+      pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
 }
 
-float CFXJSE_Value::ToFloat() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  return static_cast<float>(
-      hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0));
+float CFXJSE_Value::ToFloat(v8::Isolate* pIsolate) const {
+  return static_cast<float>(ToDouble(pIsolate));
 }
 
-double CFXJSE_Value::ToDouble() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  return hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0);
+double CFXJSE_Value::ToDouble(v8::Isolate* pIsolate) const {
+  DCHECK(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return fxv8::ReentrantToDoubleHelper(
+      pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
 }
 
-int32_t CFXJSE_Value::ToInteger() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  return static_cast<int32_t>(
-      hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0));
+int32_t CFXJSE_Value::ToInteger(v8::Isolate* pIsolate) const {
+  DCHECK(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return fxv8::ReentrantToInt32Helper(
+      pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
 }
 
-ByteString CFXJSE_Value::ToString() const {
-  ASSERT(!IsEmpty());
-  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::Local<v8::Value>::New(GetIsolate(), m_hValue);
-  v8::Local<v8::String> hString =
-      hValue->ToString(GetIsolate()->GetCurrentContext()).ToLocalChecked();
-  v8::String::Utf8Value hStringVal(GetIsolate(), hString);
-  return ByteString(*hStringVal);
+ByteString CFXJSE_Value::ToString(v8::Isolate* pIsolate) const {
+  DCHECK(!IsEmpty());
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate);
+  return fxv8::ReentrantToByteStringHelper(
+      pIsolate, v8::Local<v8::Value>::New(pIsolate, m_hValue));
 }
 
-void CFXJSE_Value::SetUndefined() {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Undefined(GetIsolate());
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetUndefined(v8::Isolate* pIsolate) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewUndefinedHelper(pIsolate));
 }
 
-void CFXJSE_Value::SetNull() {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetNull(v8::Isolate* pIsolate) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewNullHelper(pIsolate));
 }
 
-void CFXJSE_Value::SetBoolean(bool bBoolean) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Boolean::New(GetIsolate(), !!bBoolean);
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetBoolean(v8::Isolate* pIsolate, bool bBoolean) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewBooleanHelper(pIsolate, bBoolean));
 }
 
-void CFXJSE_Value::SetInteger(int32_t nInteger) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Integer::New(GetIsolate(), nInteger);
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetInteger(v8::Isolate* pIsolate, int32_t nInteger) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, nInteger));
 }
 
-void CFXJSE_Value::SetDouble(double dDouble) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue = v8::Number::New(GetIsolate(), dDouble);
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetDouble(v8::Isolate* pIsolate, double dDouble) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, dDouble));
 }
 
-void CFXJSE_Value::SetString(ByteStringView szString) {
-  CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate());
-  v8::Local<v8::Value> hValue =
-      v8::String::NewFromUtf8(GetIsolate(), szString.unterminated_c_str(),
-                              v8::NewStringType::kNormal, szString.GetLength())
-          .ToLocalChecked();
-  m_hValue.Reset(GetIsolate(), hValue);
+void CFXJSE_Value::SetString(v8::Isolate* pIsolate, ByteStringView szString) {
+  CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate);
+  m_hValue.Reset(pIsolate, fxv8::NewStringHelper(pIsolate, szString));
 }
diff --git a/fxjs/xfa/cfxjse_value.h b/fxjs/xfa/cfxjse_value.h
index 44cc58c..b8b6687 100644
--- a/fxjs/xfa/cfxjse_value.h
+++ b/fxjs/xfa/cfxjse_value.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,84 +7,91 @@
 #ifndef FXJS_XFA_CFXJSE_VALUE_H_
 #define FXJS_XFA_CFXJSE_VALUE_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "v8/include/v8.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-persistent-handle.h"
 
 class CFXJSE_Class;
 class CFXJSE_HostObject;
 
 class CFXJSE_Value {
  public:
-  explicit CFXJSE_Value(v8::Isolate* pIsolate);
+  CFXJSE_Value();
+  CFXJSE_Value(v8::Isolate* pIsolate, v8::Local<v8::Value> value);
   ~CFXJSE_Value();
 
   bool IsEmpty() const;
-  bool IsUndefined() const;
-  bool IsNull() const;
-  bool IsBoolean() const;
-  bool IsString() const;
-  bool IsNumber() const;
-  bool IsInteger() const;
-  bool IsObject() const;
-  bool IsArray() const;
-  bool IsFunction() const;
-  bool ToBoolean() const;
-  float ToFloat() const;
-  double ToDouble() const;
-  int32_t ToInteger() const;
-  ByteString ToString() const;
-  WideString ToWideString() const {
-    return WideString::FromUTF8(ToString().AsStringView());
+  bool IsUndefined(v8::Isolate* pIsolate) const;
+  bool IsNull(v8::Isolate* pIsolate) const;
+  bool IsBoolean(v8::Isolate* pIsolate) const;
+  bool IsString(v8::Isolate* pIsolate) const;
+  bool IsNumber(v8::Isolate* pIsolate) const;
+  bool IsInteger(v8::Isolate* pIsolate) const;
+  bool IsObject(v8::Isolate* pIsolate) const;
+  bool IsArray(v8::Isolate* pIsolate) const;
+  bool IsFunction(v8::Isolate* pIsolate) const;
+  bool ToBoolean(v8::Isolate* pIsolate) const;
+  float ToFloat(v8::Isolate* pIsolate) const;
+  double ToDouble(v8::Isolate* pIsolate) const;
+  int32_t ToInteger(v8::Isolate* pIsolate) const;
+  ByteString ToString(v8::Isolate* pIsolate) const;
+  WideString ToWideString(v8::Isolate* pIsolate) const {
+    return WideString::FromUTF8(ToString(pIsolate).AsStringView());
   }
-  CFXJSE_HostObject* ToHostObject() const;
+  CFXJSE_HostObject* ToHostObject(v8::Isolate* pIsolate) const;
 
-  void SetUndefined();
-  void SetNull();
-  void SetBoolean(bool bBoolean);
-  void SetInteger(int32_t nInteger);
-  void SetDouble(double dDouble);
-  void SetString(ByteStringView szString);
-  void SetFloat(float fFloat);
+  void SetUndefined(v8::Isolate* pIsolate);
+  void SetNull(v8::Isolate* pIsolate);
+  void SetBoolean(v8::Isolate* pIsolate, bool bBoolean);
+  void SetInteger(v8::Isolate* pIsolate, int32_t nInteger);
+  void SetDouble(v8::Isolate* pIsolate, double dDouble);
+  void SetString(v8::Isolate* pIsolate, ByteStringView szString);
+  void SetFloat(v8::Isolate* pIsolate, float fFloat);
 
-  void SetHostObject(CFXJSE_HostObject* lpObject, CFXJSE_Class* pClass);
-  void ClearHostObject();
+  void SetHostObject(v8::Isolate* pIsolate,
+                     CFXJSE_HostObject* pObject,
+                     CFXJSE_Class* pClass);
 
-  void SetArray(const std::vector<std::unique_ptr<CFXJSE_Value>>& values);
+  void SetArray(v8::Isolate* pIsolate,
+                const std::vector<std::unique_ptr<CFXJSE_Value>>& values);
 
-  bool GetObjectProperty(ByteStringView szPropName, CFXJSE_Value* lpPropValue);
-  bool SetObjectProperty(ByteStringView szPropName, CFXJSE_Value* lpPropValue);
-  bool GetObjectPropertyByIdx(uint32_t uPropIdx, CFXJSE_Value* lpPropValue);
-  bool DeleteObjectProperty(ByteStringView szPropName);
-  bool HasObjectOwnProperty(ByteStringView szPropName, bool bUseTypeGetter);
-  bool SetObjectOwnProperty(ByteStringView szPropName,
-                            CFXJSE_Value* lpPropValue);
-  bool SetFunctionBind(CFXJSE_Value* lpOldFunction, CFXJSE_Value* lpNewThis);
+  bool GetObjectProperty(v8::Isolate* pIsolate,
+                         ByteStringView szPropName,
+                         CFXJSE_Value* pPropValue);
+  bool SetObjectProperty(v8::Isolate* pIsolate,
+                         ByteStringView szPropName,
+                         CFXJSE_Value* pPropValue);
+  bool GetObjectPropertyByIdx(v8::Isolate* pIsolate,
+                              uint32_t uPropIdx,
+                              CFXJSE_Value* pPropValue);
+  void DeleteObjectProperty(v8::Isolate* pIsolate, ByteStringView szPropName);
+  bool SetObjectOwnProperty(v8::Isolate* pIsolate,
+                            ByteStringView szPropName,
+                            CFXJSE_Value* pPropValue);
 
-  v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); }
+  // Return empty local on error.
+  static v8::Local<v8::Function> NewBoundFunction(
+      v8::Isolate* pIsolate,
+      v8::Local<v8::Function> hOldFunction,
+      v8::Local<v8::Object> lpNewThis);
+
+  v8::Local<v8::Value> GetValue(v8::Isolate* pIsolate) const;
   const v8::Global<v8::Value>& DirectGetValue() const { return m_hValue; }
-  void ForceSetValue(v8::Local<v8::Value> hValue) {
-    m_hValue.Reset(GetIsolate(), hValue);
-  }
-  void Assign(const CFXJSE_Value* lpValue) {
-    ASSERT(lpValue);
-    if (lpValue) {
-      m_hValue.Reset(GetIsolate(), lpValue->m_hValue);
-    } else {
-      m_hValue.Reset();
-    }
+  void ForceSetValue(v8::Isolate* pIsolate, v8::Local<v8::Value> hValue) {
+    m_hValue.Reset(pIsolate, hValue);
   }
 
  private:
-  CFXJSE_Value() = delete;
   CFXJSE_Value(const CFXJSE_Value&) = delete;
   CFXJSE_Value& operator=(const CFXJSE_Value&) = delete;
 
-  UnownedPtr<v8::Isolate> const m_pIsolate;
   v8::Global<v8::Value> m_hValue;
 };
 
diff --git a/fxjs/xfa/cfxjse_value_embeddertest.cpp b/fxjs/xfa/cfxjse_value_embeddertest.cpp
index e9c39c1..c1e810b 100644
--- a/fxjs/xfa/cfxjse_value_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_value_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,34 +11,33 @@
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/xfa_js_embedder_test.h"
-#include "third_party/base/ptr_util.h"
 
 class CFXJSE_ValueEmbedderTest : public XFAJSEmbedderTest {};
 
 TEST_F(CFXJSE_ValueEmbedderTest, Empty) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  auto pValue = std::make_unique<CFXJSE_Value>();
   EXPECT_TRUE(pValue->IsEmpty());
-  EXPECT_FALSE(pValue->IsUndefined());
-  EXPECT_FALSE(pValue->IsNull());
-  EXPECT_FALSE(pValue->IsBoolean());
-  EXPECT_FALSE(pValue->IsString());
-  EXPECT_FALSE(pValue->IsNumber());
-  EXPECT_FALSE(pValue->IsObject());
-  EXPECT_FALSE(pValue->IsArray());
-  EXPECT_FALSE(pValue->IsFunction());
+  EXPECT_FALSE(pValue->IsUndefined(isolate()));
+  EXPECT_FALSE(pValue->IsNull(isolate()));
+  EXPECT_FALSE(pValue->IsBoolean(isolate()));
+  EXPECT_FALSE(pValue->IsString(isolate()));
+  EXPECT_FALSE(pValue->IsNumber(isolate()));
+  EXPECT_FALSE(pValue->IsObject(isolate()));
+  EXPECT_FALSE(pValue->IsArray(isolate()));
+  EXPECT_FALSE(pValue->IsFunction(isolate()));
 }
 
 TEST_F(CFXJSE_ValueEmbedderTest, EmptyArrayInsert) {
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
 
   // Test inserting empty values into arrays.
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
+  auto pValue = std::make_unique<CFXJSE_Value>();
   std::vector<std::unique_ptr<CFXJSE_Value>> vec;
   vec.push_back(std::move(pValue));
 
-  CFXJSE_Value array(GetIsolate());
-  array.SetArray(vec);
-  EXPECT_TRUE(array.IsArray());
+  CFXJSE_Value array;
+  array.SetArray(isolate(), vec);
+  EXPECT_TRUE(array.IsArray(isolate()));
 }
diff --git a/fxjs/xfa/cjx_boolean.cpp b/fxjs/xfa/cjx_boolean.cpp
index d44e039..a7e6f8b 100644
--- a/fxjs/xfa/cjx_boolean.cpp
+++ b/fxjs/xfa/cjx_boolean.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,9 @@
 
 #include "fxjs/xfa/cjx_boolean.h"
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_boolean.h"
 
 CJX_Boolean::CJX_Boolean(CXFA_Boolean* node) : CJX_Object(node) {}
@@ -17,30 +19,33 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Boolean::defaultValue(CFXJSE_Value* pValue,
+void CJX_Boolean::defaultValue(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value>* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
   if (!bSetting) {
-    pValue->SetBoolean(GetContent(true).EqualsASCII("1"));
+    *pValue =
+        fxv8::NewBooleanHelper(pIsolate, GetContent(true).EqualsASCII("1"));
     return;
   }
 
   ByteString newValue;
-  if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
-    newValue = pValue->ToString();
+  if (pValue && !(fxv8::IsNull(*pValue) || fxv8::IsUndefined(*pValue)))
+    newValue = fxv8::ReentrantToByteStringHelper(pIsolate, *pValue);
 
   int32_t iValue = FXSYS_atoi(newValue.c_str());
   WideString wsNewValue(iValue == 0 ? L"0" : L"1");
   WideString wsFormatValue(wsNewValue);
-  CXFA_Node* pContainerNode = ToNode(GetXFAObject())->GetContainerNode();
+  CXFA_Node* pContainerNode = GetXFANode()->GetContainerNode();
   if (pContainerNode)
     wsFormatValue = pContainerNode->GetFormatDataValue(wsNewValue);
 
   SetContent(wsNewValue, wsFormatValue, true, true, true);
 }
 
-void CJX_Boolean::value(CFXJSE_Value* pValue,
+void CJX_Boolean::value(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
+  defaultValue(pIsolate, pValue, bSetting, eAttribute);
 }
diff --git a/fxjs/xfa/cjx_boolean.h b/fxjs/xfa/cjx_boolean.h
index d2a85ba..7fc2111 100644
--- a/fxjs/xfa/cjx_boolean.h
+++ b/fxjs/xfa/cjx_boolean.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Boolean final : public CJX_Object {
  public:
-  explicit CJX_Boolean(CXFA_Boolean* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Boolean() override;
 
   // CJX_Object:
@@ -24,6 +24,8 @@
   JSE_PROP(value);
 
  private:
+  explicit CJX_Boolean(CXFA_Boolean* node);
+
   using Type__ = CJX_Boolean;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_container.cpp b/fxjs/xfa/cjx_container.cpp
index 44b1ff8..d6cabac 100644
--- a/fxjs/xfa/cjx_container.cpp
+++ b/fxjs/xfa/cjx_container.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,8 @@
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_field.h"
@@ -23,23 +25,24 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Container::~CJX_Container() {}
+CJX_Container::~CJX_Container() = default;
 
 bool CJX_Container::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Container::getDelta(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Container::getDeltas(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  auto* pEngine = static_cast<CFXJSE_Engine*>(runtime);
-  return CJS_Result::Success(pEngine->NewXFAObject(
-      new CXFA_ArrayNodeList(GetDocument()),
-      GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
+  CXFA_Document* pDoc = GetDocument();
+  auto* pList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc);
+  pDoc->GetNodeOwner()->PersistList(pList);
+  return CJS_Result::Success(runtime->NewNormalXFAObject(pList));
 }
diff --git a/fxjs/xfa/cjx_container.h b/fxjs/xfa/cjx_container.h
index 51675e5..b44dd72 100644
--- a/fxjs/xfa/cjx_container.h
+++ b/fxjs/xfa/cjx_container.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Container : public CJX_Node {
  public:
-  explicit CJX_Container(CXFA_Node* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Container() override;
 
   // CJX_Object:
@@ -23,6 +23,9 @@
   JSE_METHOD(getDelta);
   JSE_METHOD(getDeltas);
 
+ protected:
+  explicit CJX_Container(CXFA_Node* node);
+
  private:
   using Type__ = CJX_Container;
   using ParentType__ = CJX_Node;
diff --git a/fxjs/xfa/cjx_datawindow.cpp b/fxjs/xfa/cjx_datawindow.cpp
index d165140..10f8365 100644
--- a/fxjs/xfa/cjx_datawindow.cpp
+++ b/fxjs/xfa/cjx_datawindow.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,48 +22,52 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_DataWindow::~CJX_DataWindow() {}
+CJX_DataWindow::~CJX_DataWindow() = default;
 
 bool CJX_DataWindow::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_DataWindow::moveCurrentRecord(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_DataWindow::record(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_DataWindow::gotoRecord(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_DataWindow::isRecordGroup(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
-void CJX_DataWindow::recordsBefore(CFXJSE_Value* pValue,
+void CJX_DataWindow::recordsBefore(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {}
 
-void CJX_DataWindow::currentRecordNumber(CFXJSE_Value* pValue,
+void CJX_DataWindow::currentRecordNumber(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Value>* pValue,
                                          bool bSetting,
                                          XFA_Attribute eAttribute) {}
 
-void CJX_DataWindow::recordsAfter(CFXJSE_Value* pValue,
+void CJX_DataWindow::recordsAfter(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {}
 
-void CJX_DataWindow::isDefined(CFXJSE_Value* pValue,
+void CJX_DataWindow::isDefined(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value>* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_datawindow.h b/fxjs/xfa/cjx_datawindow.h
index 6f3ad54..df5092d 100644
--- a/fxjs/xfa/cjx_datawindow.h
+++ b/fxjs/xfa/cjx_datawindow.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,12 +11,11 @@
 #include "fxjs/xfa/jse_define.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
-class CFXJSE_Value;
 class CScript_DataWindow;
 
 class CJX_DataWindow final : public CJX_Object {
  public:
-  explicit CJX_DataWindow(CScript_DataWindow* window);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_DataWindow() override;
 
   // CJX_Object:
@@ -33,6 +32,8 @@
   JSE_PROP(recordsBefore);
 
  private:
+  explicit CJX_DataWindow(CScript_DataWindow* window);
+
   using Type__ = CJX_DataWindow;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_delta.cpp b/fxjs/xfa/cjx_delta.cpp
index 3ad20a6..266ddef 100644
--- a/fxjs/xfa/cjx_delta.cpp
+++ b/fxjs/xfa/cjx_delta.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,13 +18,13 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Delta::~CJX_Delta() {}
+CJX_Delta::~CJX_Delta() = default;
 
 bool CJX_Delta::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Result CJX_Delta::restore(CFX_V8* runtime,
+CJS_Result CJX_Delta::restore(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -32,14 +32,17 @@
   return CJS_Result::Success();
 }
 
-void CJX_Delta::currentValue(CFXJSE_Value* pValue,
+void CJX_Delta::currentValue(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value>* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {}
 
-void CJX_Delta::savedValue(CFXJSE_Value* pValue,
+void CJX_Delta::savedValue(v8::Isolate* pIsolate,
+                           v8::Local<v8::Value>* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {}
 
-void CJX_Delta::target(CFXJSE_Value* pValue,
+void CJX_Delta::target(v8::Isolate* pIsolate,
+                       v8::Local<v8::Value>* pValue,
                        bool bSetting,
                        XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_delta.h b/fxjs/xfa/cjx_delta.h
index 716dd1e..1862558 100644
--- a/fxjs/xfa/cjx_delta.h
+++ b/fxjs/xfa/cjx_delta.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Delta final : public CJX_Object {
  public:
-  explicit CJX_Delta(CXFA_Delta* delta);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Delta() override;
 
   // CJX_Object:
@@ -27,6 +27,8 @@
   JSE_PROP(target);
 
  private:
+  explicit CJX_Delta(CXFA_Delta* delta);
+
   using Type__ = CJX_Delta;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_desc.cpp b/fxjs/xfa/cjx_desc.cpp
index 2bbff2b..792f076 100644
--- a/fxjs/xfa/cjx_desc.cpp
+++ b/fxjs/xfa/cjx_desc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_desc.h"
 
 const CJX_MethodSpec CJX_Desc::MethodSpecs[] = {{"metadata", metadata_static}};
@@ -19,13 +20,13 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Desc::~CJX_Desc() {}
+CJX_Desc::~CJX_Desc() = default;
 
 bool CJX_Desc::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Result CJX_Desc::metadata(CFX_V8* runtime,
+CJS_Result CJX_Desc::metadata(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 0 && params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_desc.h b/fxjs/xfa/cjx_desc.h
index 62cdec8..5474d56 100644
--- a/fxjs/xfa/cjx_desc.h
+++ b/fxjs/xfa/cjx_desc.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Desc final : public CJX_Node {
  public:
-  explicit CJX_Desc(CXFA_Desc* desc);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Desc() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_METHOD(metadata);
 
  private:
+  explicit CJX_Desc(CXFA_Desc* desc);
+
   using Type__ = CJX_Desc;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_draw.cpp b/fxjs/xfa/cjx_draw.cpp
index 4e8ddc1..82307b0 100644
--- a/fxjs/xfa/cjx_draw.cpp
+++ b/fxjs/xfa/cjx_draw.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,11 @@
 
 #include "fxjs/xfa/cjx_draw.h"
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-primitive.h"
+#include "v8/include/v8-value.h"
 #include "xfa/fxfa/parser/cxfa_draw.h"
 
 CJX_Draw::CJX_Draw(CXFA_Draw* node) : CJX_Container(node) {}
@@ -17,32 +21,33 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Draw::rawValue(CFXJSE_Value* pValue,
+void CJX_Draw::rawValue(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
+  defaultValue(pIsolate, pValue, bSetting, eAttribute);
 }
 
-void CJX_Draw::defaultValue(CFXJSE_Value* pValue,
+void CJX_Draw::defaultValue(v8::Isolate* pIsolate,
+                            v8::Local<v8::Value>* pValue,
                             bool bSetting,
                             XFA_Attribute eAttribute) {
   if (!bSetting) {
-    WideString content = GetContent(true);
-    if (content.IsEmpty())
-      pValue->SetNull();
-    else
-      pValue->SetString(content.ToUTF8().AsStringView());
-
+    ByteString content = GetContent(true).ToUTF8();
+    *pValue = content.IsEmpty()
+                  ? fxv8::NewNullHelper(pIsolate).As<v8::Value>()
+                  : fxv8::NewStringHelper(pIsolate, content.AsStringView())
+                        .As<v8::Value>();
     return;
   }
 
-  if (!pValue || !pValue->IsString())
+  if (!pValue || !fxv8::IsString(*pValue))
     return;
 
-  ASSERT(GetXFANode()->IsWidgetReady());
+  DCHECK(GetXFANode()->IsWidgetReady());
   if (GetXFANode()->GetFFWidgetType() != XFA_FFWidgetType::kText)
     return;
 
-  WideString wsNewValue = pValue->ToWideString();
+  WideString wsNewValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
   SetContent(wsNewValue, wsNewValue, true, true, true);
 }
diff --git a/fxjs/xfa/cjx_draw.h b/fxjs/xfa/cjx_draw.h
index bf3c3a7..a813f15 100644
--- a/fxjs/xfa/cjx_draw.h
+++ b/fxjs/xfa/cjx_draw.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Draw final : public CJX_Container {
  public:
-  explicit CJX_Draw(CXFA_Draw* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Draw() override;
 
   // CJX_Object:
@@ -24,6 +24,8 @@
   JSE_PROP(rawValue);
 
  private:
+  explicit CJX_Draw(CXFA_Draw* node);
+
   using Type__ = CJX_Draw;
   using ParentType__ = CJX_Container;
 
diff --git a/fxjs/xfa/cjx_encrypt.cpp b/fxjs/xfa/cjx_encrypt.cpp
index f50133d..51e02d6 100644
--- a/fxjs/xfa/cjx_encrypt.cpp
+++ b/fxjs/xfa/cjx_encrypt.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,6 +16,7 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Encrypt::format(CFXJSE_Value* pValue,
+void CJX_Encrypt::format(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_encrypt.h b/fxjs/xfa/cjx_encrypt.h
index 826bc1f..c7d4a46 100644
--- a/fxjs/xfa/cjx_encrypt.h
+++ b/fxjs/xfa/cjx_encrypt.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Encrypt final : public CJX_Node {
  public:
-  explicit CJX_Encrypt(CXFA_Encrypt* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Encrypt() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(format);
 
  private:
+  explicit CJX_Encrypt(CXFA_Encrypt* node);
+
   using Type__ = CJX_Encrypt;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_eventpseudomodel.cpp b/fxjs/xfa/cjx_eventpseudomodel.cpp
index d037eb7..72309dc 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.cpp
+++ b/fxjs/xfa/cjx_eventpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,37 +9,48 @@
 #include <algorithm>
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
-#include "xfa/fxfa/cxfa_ffwidgethandler.h"
 #include "xfa/fxfa/parser/cscript_eventpseudomodel.h"
 
 namespace {
 
-void StringProperty(CFXJSE_Value* pReturn, WideString* wsValue, bool bSetting) {
+void StringProperty(v8::Isolate* pIsolate,
+                    v8::Local<v8::Value>* pReturn,
+                    WideString* wsValue,
+                    bool bSetting) {
   if (bSetting) {
-    *wsValue = pReturn->ToWideString();
+    *wsValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pReturn);
     return;
   }
-  pReturn->SetString(wsValue->ToUTF8().AsStringView());
+  *pReturn = fxv8::NewStringHelper(pIsolate, wsValue->ToUTF8().AsStringView());
 }
 
-void IntegerProperty(CFXJSE_Value* pReturn, int32_t* iValue, bool bSetting) {
+void IntegerProperty(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pReturn,
+                     int32_t* iValue,
+                     bool bSetting) {
   if (bSetting) {
-    *iValue = pReturn->ToInteger();
+    *iValue = fxv8::ReentrantToInt32Helper(pIsolate, *pReturn);
     return;
   }
-  pReturn->SetInteger(*iValue);
+  *pReturn = fxv8::NewNumberHelper(pIsolate, *iValue);
 }
 
-void BooleanProperty(CFXJSE_Value* pReturn, bool* bValue, bool bSetting) {
+void BooleanProperty(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pReturn,
+                     bool* bValue,
+                     bool bSetting) {
   if (bSetting) {
-    *bValue = pReturn->ToBoolean();
+    *bValue = fxv8::ReentrantToBooleanHelper(pIsolate, *pReturn);
     return;
   }
-  pReturn->SetBoolean(*bValue);
+  *pReturn = fxv8::NewBooleanHelper(pIsolate, *bValue);
 }
 
 }  // namespace
@@ -53,55 +64,63 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_EventPseudoModel::~CJX_EventPseudoModel() {}
+CJX_EventPseudoModel::~CJX_EventPseudoModel() = default;
 
 bool CJX_EventPseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_EventPseudoModel::cancelAction(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::cancelAction(v8::Isolate* pIsolate,
+                                        v8::Local<v8::Value>* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::CancelAction, bSetting);
+  Property(pIsolate, pValue, XFA_Event::CancelAction, bSetting);
 }
 
-void CJX_EventPseudoModel::change(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::change(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Change, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Change, bSetting);
 }
 
-void CJX_EventPseudoModel::commitKey(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::commitKey(v8::Isolate* pIsolate,
+                                     v8::Local<v8::Value>* pValue,
                                      bool bSetting,
                                      XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::CommitKey, bSetting);
+  Property(pIsolate, pValue, XFA_Event::CommitKey, bSetting);
 }
 
-void CJX_EventPseudoModel::fullText(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::fullText(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::FullText, bSetting);
+  Property(pIsolate, pValue, XFA_Event::FullText, bSetting);
 }
 
-void CJX_EventPseudoModel::keyDown(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::keyDown(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Keydown, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Keydown, bSetting);
 }
 
-void CJX_EventPseudoModel::modifier(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::modifier(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Modifier, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Modifier, bSetting);
 }
 
-void CJX_EventPseudoModel::newContentType(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::newContentType(v8::Isolate* pIsolate,
+                                          v8::Local<v8::Value>* pValue,
                                           bool bSetting,
                                           XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::NewContentType, bSetting);
+  Property(pIsolate, pValue, XFA_Event::NewContentType, bSetting);
 }
 
-void CJX_EventPseudoModel::newText(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::newText(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   if (bSetting)
@@ -112,65 +131,75 @@
   if (!pEventParam)
     return;
 
-  pValue->SetString(pEventParam->GetNewText().ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, pEventParam->GetNewText().ToUTF8().AsStringView());
 }
 
-void CJX_EventPseudoModel::prevContentType(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::prevContentType(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Value>* pValue,
                                            bool bSetting,
                                            XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::PreviousContentType, bSetting);
+  Property(pIsolate, pValue, XFA_Event::PreviousContentType, bSetting);
 }
 
-void CJX_EventPseudoModel::prevText(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::prevText(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::PreviousText, bSetting);
+  Property(pIsolate, pValue, XFA_Event::PreviousText, bSetting);
 }
 
-void CJX_EventPseudoModel::reenter(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::reenter(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Reenter, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Reenter, bSetting);
 }
 
-void CJX_EventPseudoModel::selEnd(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::selEnd(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::SelectionEnd, bSetting);
+  Property(pIsolate, pValue, XFA_Event::SelectionEnd, bSetting);
 }
 
-void CJX_EventPseudoModel::selStart(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::selStart(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::SelectionStart, bSetting);
+  Property(pIsolate, pValue, XFA_Event::SelectionStart, bSetting);
 }
 
-void CJX_EventPseudoModel::shift(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::shift(v8::Isolate* pIsolate,
+                                 v8::Local<v8::Value>* pValue,
                                  bool bSetting,
                                  XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Shift, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Shift, bSetting);
 }
 
-void CJX_EventPseudoModel::soapFaultCode(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::soapFaultCode(v8::Isolate* pIsolate,
+                                         v8::Local<v8::Value>* pValue,
                                          bool bSetting,
                                          XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::SoapFaultCode, bSetting);
+  Property(pIsolate, pValue, XFA_Event::SoapFaultCode, bSetting);
 }
 
-void CJX_EventPseudoModel::soapFaultString(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::soapFaultString(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Value>* pValue,
                                            bool bSetting,
                                            XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::SoapFaultString, bSetting);
+  Property(pIsolate, pValue, XFA_Event::SoapFaultString, bSetting);
 }
 
-void CJX_EventPseudoModel::target(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::target(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
-  Property(pValue, XFA_Event::Target, bSetting);
+  Property(pIsolate, pValue, XFA_Event::Target, bSetting);
 }
 
 CJS_Result CJX_EventPseudoModel::emit(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
   CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
@@ -181,16 +210,12 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_FFWidgetHandler* pWidgetHandler = pNotify->GetWidgetHandler();
-  if (!pWidgetHandler)
-    return CJS_Result::Success();
-
-  pWidgetHandler->ProcessEvent(pEventParam->m_pTarget.Get(), pEventParam);
+  pNotify->HandleWidgetEvent(pScriptContext->GetEventTarget(), pEventParam);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_EventPseudoModel::reset(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
   CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
@@ -200,7 +225,8 @@
   return CJS_Result::Success();
 }
 
-void CJX_EventPseudoModel::Property(CFXJSE_Value* pValue,
+void CJX_EventPseudoModel::Property(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     XFA_Event dwFlag,
                                     bool bSetting) {
   // Only the cancelAction, selStart, selEnd and change properties are writable.
@@ -217,65 +243,70 @@
 
   switch (dwFlag) {
     case XFA_Event::CancelAction:
-      BooleanProperty(pValue, &pEventParam->m_bCancelAction, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bCancelAction,
+                      bSetting);
       break;
     case XFA_Event::Change:
-      StringProperty(pValue, &pEventParam->m_wsChange, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsChange, bSetting);
       break;
     case XFA_Event::CommitKey:
-      IntegerProperty(pValue, &pEventParam->m_iCommitKey, bSetting);
+      IntegerProperty(pIsolate, pValue, &pEventParam->m_iCommitKey, bSetting);
       break;
     case XFA_Event::FullText:
-      StringProperty(pValue, &pEventParam->m_wsFullText, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsFullText, bSetting);
       break;
     case XFA_Event::Keydown:
-      BooleanProperty(pValue, &pEventParam->m_bKeyDown, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bKeyDown, bSetting);
       break;
     case XFA_Event::Modifier:
-      BooleanProperty(pValue, &pEventParam->m_bModifier, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bModifier, bSetting);
       break;
     case XFA_Event::NewContentType:
-      StringProperty(pValue, &pEventParam->m_wsNewContentType, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsNewContentType,
+                     bSetting);
       break;
     case XFA_Event::NewText:
       NOTREACHED();
       break;
     case XFA_Event::PreviousContentType:
-      StringProperty(pValue, &pEventParam->m_wsPrevContentType, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsPrevContentType,
+                     bSetting);
       break;
     case XFA_Event::PreviousText:
-      StringProperty(pValue, &pEventParam->m_wsPrevText, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsPrevText, bSetting);
       break;
     case XFA_Event::Reenter:
-      BooleanProperty(pValue, &pEventParam->m_bReenter, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bReenter, bSetting);
       break;
     case XFA_Event::SelectionEnd:
-      IntegerProperty(pValue, &pEventParam->m_iSelEnd, bSetting);
+      IntegerProperty(pIsolate, pValue, &pEventParam->m_iSelEnd, bSetting);
 
       pEventParam->m_iSelEnd = std::max(0, pEventParam->m_iSelEnd);
-      pEventParam->m_iSelEnd =
-          std::min(static_cast<size_t>(pEventParam->m_iSelEnd),
-                   pEventParam->m_wsPrevText.GetLength());
+      pEventParam->m_iSelEnd = std::min(
+          pEventParam->m_iSelEnd, pdfium::base::checked_cast<int32_t>(
+                                      pEventParam->m_wsPrevText.GetLength()));
       pEventParam->m_iSelStart =
           std::min(pEventParam->m_iSelStart, pEventParam->m_iSelEnd);
       break;
     case XFA_Event::SelectionStart:
-      IntegerProperty(pValue, &pEventParam->m_iSelStart, bSetting);
+      IntegerProperty(pIsolate, pValue, &pEventParam->m_iSelStart, bSetting);
       pEventParam->m_iSelStart = std::max(0, pEventParam->m_iSelStart);
-      pEventParam->m_iSelStart =
-          std::min(static_cast<size_t>(pEventParam->m_iSelStart),
-                   pEventParam->m_wsPrevText.GetLength());
+      pEventParam->m_iSelStart = std::min(
+          pEventParam->m_iSelStart, pdfium::base::checked_cast<int32_t>(
+                                        pEventParam->m_wsPrevText.GetLength()));
       pEventParam->m_iSelEnd =
           std::max(pEventParam->m_iSelStart, pEventParam->m_iSelEnd);
       break;
     case XFA_Event::Shift:
-      BooleanProperty(pValue, &pEventParam->m_bShift, bSetting);
+      BooleanProperty(pIsolate, pValue, &pEventParam->m_bShift, bSetting);
       break;
     case XFA_Event::SoapFaultCode:
-      StringProperty(pValue, &pEventParam->m_wsSoapFaultCode, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsSoapFaultCode,
+                     bSetting);
       break;
     case XFA_Event::SoapFaultString:
-      StringProperty(pValue, &pEventParam->m_wsSoapFaultString, bSetting);
+      StringProperty(pIsolate, pValue, &pEventParam->m_wsSoapFaultString,
+                     bSetting);
       break;
     case XFA_Event::Target:
     default:
diff --git a/fxjs/xfa/cjx_eventpseudomodel.h b/fxjs/xfa/cjx_eventpseudomodel.h
index 6f3cc84..3be881a 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.h
+++ b/fxjs/xfa/cjx_eventpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,7 +10,6 @@
 #include "fxjs/xfa/cjx_object.h"
 #include "fxjs/xfa/jse_define.h"
 
-class CFXJSE_Value;
 class CScript_EventPseudoModel;
 
 enum class XFA_Event {
@@ -35,7 +34,7 @@
 
 class CJX_EventPseudoModel final : public CJX_Object {
  public:
-  explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_EventPseudoModel() override;
 
   // CJX_Object:
@@ -63,13 +62,18 @@
   JSE_PROP(target);
 
  private:
+  explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model);
+
   using Type__ = CJX_EventPseudoModel;
   using ParentType__ = CJX_Object;
 
   static const TypeTag static_type__ = TypeTag::EventPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 
-  void Property(CFXJSE_Value* pValue, XFA_Event dwFlag, bool bSetting);
+  void Property(v8::Isolate* pIsolate,
+                v8::Local<v8::Value>* pValue,
+                XFA_Event dwFlag,
+                bool bSetting);
 };
 
 #endif  // FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_exclgroup.cpp b/fxjs/xfa/cjx_exclgroup.cpp
index 00bf539..11e6982 100644
--- a/fxjs/xfa/cjx_exclgroup.cpp
+++ b/fxjs/xfa/cjx_exclgroup.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,11 @@
 
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/fxfa.h"
@@ -28,14 +30,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_ExclGroup::~CJX_ExclGroup() {}
+CJX_ExclGroup::~CJX_ExclGroup() = default;
 
 bool CJX_ExclGroup::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_ExclGroup::execEvent(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -46,7 +48,7 @@
 }
 
 CJS_Result CJX_ExclGroup::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -59,7 +61,7 @@
 }
 
 CJS_Result CJX_ExclGroup::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -72,7 +74,7 @@
 }
 
 CJS_Result CJX_ExclGroup::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -88,7 +90,7 @@
 }
 
 CJS_Result CJX_ExclGroup::selectedMember(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -102,20 +104,18 @@
     pReturnNode = node->GetSelectedMember();
   } else {
     pReturnNode = node->SetSelectedMember(
-        runtime->ToWideString(params[0]).AsStringView(), true);
+        runtime->ToWideString(params[0]).AsStringView());
   }
   if (!pReturnNode)
     return CJS_Result::Success(runtime->NewNull());
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pReturnNode);
-
   return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pReturnNode));
 }
 
-void CJX_ExclGroup::defaultValue(CFXJSE_Value* pValue,
+void CJX_ExclGroup::defaultValue(v8::Isolate* pIsolate,
+                                 v8::Local<v8::Value>* pValue,
                                  bool bSetting,
                                  XFA_Attribute eAttribute) {
   CXFA_Node* node = GetXFANode();
@@ -123,33 +123,37 @@
     return;
 
   if (bSetting) {
-    node->SetSelectedMemberByValue(pValue->ToWideString().AsStringView(), true,
-                                   true, true);
+    node->SetSelectedMemberByValue(
+        fxv8::ReentrantToWideStringHelper(pIsolate, *pValue).AsStringView(),
+        true, true, true);
     return;
   }
 
   WideString wsValue = GetContent(true);
   XFA_VERSION curVersion = GetDocument()->GetCurVersionMode();
   if (wsValue.IsEmpty() && curVersion >= XFA_VERSION_300) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
     return;
   }
-  pValue->SetString(wsValue.ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, wsValue.ToUTF8().AsStringView());
 }
 
-void CJX_ExclGroup::rawValue(CFXJSE_Value* pValue,
+void CJX_ExclGroup::rawValue(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value>* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
+  defaultValue(pIsolate, pValue, bSetting, eAttribute);
 }
 
-void CJX_ExclGroup::transient(CFXJSE_Value* pValue,
+void CJX_ExclGroup::transient(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {}
 
-void CJX_ExclGroup::errorText(CFXJSE_Value* pValue,
+void CJX_ExclGroup::errorText(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   if (bSetting)
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
 }
diff --git a/fxjs/xfa/cjx_exclgroup.h b/fxjs/xfa/cjx_exclgroup.h
index b459808..f6a983c 100644
--- a/fxjs/xfa/cjx_exclgroup.h
+++ b/fxjs/xfa/cjx_exclgroup.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_ExclGroup final : public CJX_Node {
  public:
-  explicit CJX_ExclGroup(CXFA_ExclGroup* group);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_ExclGroup() override;
 
   // CJX_Object:
@@ -32,6 +32,8 @@
   JSE_PROP(transient);
 
  private:
+  explicit CJX_ExclGroup(CXFA_ExclGroup* group);
+
   using Type__ = CJX_ExclGroup;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_extras.cpp b/fxjs/xfa/cjx_extras.cpp
index a2e2eea..74cdbba 100644
--- a/fxjs/xfa/cjx_extras.cpp
+++ b/fxjs/xfa/cjx_extras.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,6 +16,7 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Extras::type(CFXJSE_Value* pValue,
+void CJX_Extras::type(v8::Isolate* pIsolate,
+                      v8::Local<v8::Value>* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_extras.h b/fxjs/xfa/cjx_extras.h
index 0723575..6a1b212 100644
--- a/fxjs/xfa/cjx_extras.h
+++ b/fxjs/xfa/cjx_extras.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Extras final : public CJX_Node {
  public:
-  explicit CJX_Extras(CXFA_Extras* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Extras() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(type);
 
  private:
+  explicit CJX_Extras(CXFA_Extras* node);
+
   using Type__ = CJX_Extras;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_field.cpp b/fxjs/xfa/cjx_field.cpp
index ef334a3..8d158dc 100644
--- a/fxjs/xfa/cjx_field.cpp
+++ b/fxjs/xfa/cjx_field.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,10 @@
 #include <vector>
 
 #include "fxjs/cfx_v8.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
@@ -37,14 +39,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Field::~CJX_Field() {}
+CJX_Field::~CJX_Field() = default;
 
 bool CJX_Field::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Field::clearItems(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_Node* node = GetXFANode();
   if (node->IsWidgetReady())
@@ -53,7 +55,7 @@
 }
 
 CJS_Result CJX_Field::execEvent(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -69,7 +71,7 @@
 }
 
 CJS_Result CJX_Field::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -83,7 +85,7 @@
 }
 
 CJS_Result CJX_Field::deleteItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -97,7 +99,7 @@
 }
 
 CJS_Result CJX_Field::getSaveItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -110,8 +112,8 @@
   if (!node->IsWidgetReady())
     return CJS_Result::Success(runtime->NewNull());
 
-  Optional<WideString> value = node->GetChoiceListItem(iIndex, true);
-  if (!value)
+  absl::optional<WideString> value = node->GetChoiceListItem(iIndex, true);
+  if (!value.has_value())
     return CJS_Result::Success(runtime->NewNull());
 
   return CJS_Result::Success(
@@ -119,7 +121,7 @@
 }
 
 CJS_Result CJX_Field::boundItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -135,7 +137,7 @@
 }
 
 CJS_Result CJX_Field::getItemState(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -149,7 +151,7 @@
 }
 
 CJS_Result CJX_Field::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -163,7 +165,7 @@
 }
 
 CJS_Result CJX_Field::getDisplayItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -176,8 +178,8 @@
   if (!node->IsWidgetReady())
     return CJS_Result::Success(runtime->NewNull());
 
-  Optional<WideString> value = node->GetChoiceListItem(iIndex, false);
-  if (!value)
+  absl::optional<WideString> value = node->GetChoiceListItem(iIndex, false);
+  if (!value.has_value())
     return CJS_Result::Success(runtime->NewNull());
 
   return CJS_Result::Success(
@@ -185,7 +187,7 @@
 }
 
 CJS_Result CJX_Field::setItemState(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -196,16 +198,16 @@
 
   int32_t iIndex = runtime->ToInt32(params[0]);
   if (runtime->ToInt32(params[1]) != 0) {
-    node->SetItemState(iIndex, true, true, true, true);
+    node->SetItemState(iIndex, true, true, true);
     return CJS_Result::Success();
   }
   if (node->GetItemState(iIndex))
-    node->SetItemState(iIndex, false, true, true, true);
+    node->SetItemState(iIndex, false, true, true);
 
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Field::addItem(CFX_V8* runtime,
+CJS_Result CJX_Field::addItem(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1 && params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -227,7 +229,7 @@
 }
 
 CJS_Result CJX_Field::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -242,7 +244,8 @@
       runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
-void CJX_Field::defaultValue(CFXJSE_Value* pValue,
+void CJX_Field::defaultValue(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value>* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
   CXFA_Node* xfaNode = GetXFANode();
@@ -252,12 +255,12 @@
   if (bSetting) {
     if (pValue) {
       xfaNode->SetPreNull(xfaNode->IsNull());
-      xfaNode->SetIsNull(pValue->IsNull());
+      xfaNode->SetIsNull(fxv8::IsNull(*pValue));
     }
 
     WideString wsNewText;
-    if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
-      wsNewText = pValue->ToWideString();
+    if (pValue && !(fxv8::IsNull(*pValue) || fxv8::IsUndefined(*pValue)))
+      wsNewText = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
     if (xfaNode->GetUIChildNode()->GetElementType() == XFA_Element::NumericEdit)
       wsNewText = xfaNode->NumericLimit(wsNewText);
 
@@ -272,7 +275,7 @@
 
   WideString content = GetContent(true);
   if (content.IsEmpty()) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
     return;
   }
 
@@ -282,24 +285,27 @@
     if (xfaNode->GetUIChildNode()->GetElementType() ==
             XFA_Element::NumericEdit &&
         (pNode->JSObject()->GetInteger(XFA_Attribute::FracDigits) == -1)) {
-      pValue->SetString(content.ToUTF8().AsStringView());
+      *pValue =
+          fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
     } else {
       CFGAS_Decimal decimal(content.AsStringView());
-      pValue->SetFloat(decimal.ToFloat());
+      *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat());
     }
   } else if (pNode && pNode->GetElementType() == XFA_Element::Integer) {
-    pValue->SetInteger(FXSYS_wtoi(content.c_str()));
+    *pValue = fxv8::NewNumberHelper(pIsolate, FXSYS_wtoi(content.c_str()));
   } else if (pNode && pNode->GetElementType() == XFA_Element::Boolean) {
-    pValue->SetBoolean(FXSYS_wtoi(content.c_str()) != 0);
+    *pValue =
+        fxv8::NewBooleanHelper(pIsolate, FXSYS_wtoi(content.c_str()) != 0);
   } else if (pNode && pNode->GetElementType() == XFA_Element::Float) {
     CFGAS_Decimal decimal(content.AsStringView());
-    pValue->SetFloat(decimal.ToFloat());
+    *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat());
   } else {
-    pValue->SetString(content.ToUTF8().AsStringView());
+    *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
   }
 }
 
-void CJX_Field::editValue(CFXJSE_Value* pValue,
+void CJX_Field::editValue(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {
   CXFA_Node* node = GetXFANode();
@@ -307,20 +313,24 @@
     return;
 
   if (bSetting) {
-    node->SetValue(XFA_VALUEPICTURE_Edit, pValue->ToWideString());
+    node->SetValue(XFA_ValuePicture::kEdit,
+                   fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     return;
   }
-  pValue->SetString(
-      node->GetValue(XFA_VALUEPICTURE_Edit).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate,
+      node->GetValue(XFA_ValuePicture::kEdit).ToUTF8().AsStringView());
 }
 
-void CJX_Field::formatMessage(CFXJSE_Value* pValue,
+void CJX_Field::formatMessage(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
-  ScriptSomMessage(pValue, bSetting, XFA_SOM_FormatMessage);
+  ScriptSomMessage(pIsolate, pValue, bSetting, SOMMessageType::kFormatMessage);
 }
 
-void CJX_Field::formattedValue(CFXJSE_Value* pValue,
+void CJX_Field::formattedValue(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value>* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
   CXFA_Node* node = GetXFANode();
@@ -328,40 +338,44 @@
     return;
 
   if (bSetting) {
-    node->SetValue(XFA_VALUEPICTURE_Display, pValue->ToWideString());
+    node->SetValue(XFA_ValuePicture::kDisplay,
+                   fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     return;
   }
-  pValue->SetString(
-      node->GetValue(XFA_VALUEPICTURE_Display).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate,
+      node->GetValue(XFA_ValuePicture::kDisplay).ToUTF8().AsStringView());
 }
 
-void CJX_Field::length(CFXJSE_Value* pValue,
+void CJX_Field::length(v8::Isolate* pIsolate,
+                       v8::Local<v8::Value>* pValue,
                        bool bSetting,
                        XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   CXFA_Node* node = GetXFANode();
-  if (!node->IsWidgetReady()) {
-    pValue->SetInteger(0);
-    return;
-  }
-  pValue->SetInteger(node->CountChoiceListItems(true));
+  *pValue = fxv8::NewNumberHelper(
+      pIsolate, node->IsWidgetReady() ? pdfium::base::checked_cast<int>(
+                                            node->CountChoiceListItems(true))
+                                      : 0);
 }
 
-void CJX_Field::parentSubform(CFXJSE_Value* pValue,
+void CJX_Field::parentSubform(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetNull();
+  *pValue = fxv8::NewNullHelper(pIsolate);
 }
 
-void CJX_Field::selectedIndex(CFXJSE_Value* pValue,
+void CJX_Field::selectedIndex(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   CXFA_Node* node = GetXFANode();
@@ -369,21 +383,22 @@
     return;
 
   if (!bSetting) {
-    pValue->SetInteger(node->GetSelectedItem(0));
+    *pValue = fxv8::NewNumberHelper(pIsolate, node->GetSelectedItem(0));
     return;
   }
 
-  int32_t iIndex = pValue->ToInteger();
+  int32_t iIndex = fxv8::ReentrantToInt32Helper(pIsolate, *pValue);
   if (iIndex == -1) {
     node->ClearAllSelections();
     return;
   }
 
-  node->SetItemState(iIndex, true, true, true, true);
+  node->SetItemState(iIndex, true, true, true);
 }
 
-void CJX_Field::rawValue(CFXJSE_Value* pValue,
+void CJX_Field::rawValue(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
-  defaultValue(pValue, bSetting, eAttribute);
+  defaultValue(pIsolate, pValue, bSetting, eAttribute);
 }
diff --git a/fxjs/xfa/cjx_field.h b/fxjs/xfa/cjx_field.h
index 6e16e7a..5628fc4 100644
--- a/fxjs/xfa/cjx_field.h
+++ b/fxjs/xfa/cjx_field.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Field final : public CJX_Container {
  public:
-  explicit CJX_Field(CXFA_Field* field);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Field() override;
 
   // CJX_Object:
@@ -43,6 +43,8 @@
   JSE_PROP(selectedIndex);
 
  private:
+  explicit CJX_Field(CXFA_Field* field);
+
   using Type__ = CJX_Field;
   using ParentType__ = CJX_Container;
 
diff --git a/fxjs/xfa/cjx_form.cpp b/fxjs/xfa/cjx_form.cpp
index 07e8602..4dab966 100644
--- a/fxjs/xfa/cjx_form.cpp
+++ b/fxjs/xfa/cjx_form.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,12 @@
 
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
@@ -29,43 +32,42 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Form::~CJX_Form() {}
+CJX_Form::~CJX_Form() = default;
 
 bool CJX_Form::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Form::formNodes(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pDataNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  CXFA_Node* pDataNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pDataNode)
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  CXFA_ArrayNodeList* pFormNodes = new CXFA_ArrayNodeList(GetDocument());
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pFormNodes);
+  CXFA_Document* pDoc = GetDocument();
+  auto* pFormNodes = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc);
+  pDoc->GetNodeOwner()->PersistList(pFormNodes);
 
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  v8::Local<v8::Value> value = runtime->GetOrCreateJSBindingFromMap(pFormNodes);
+  return CJS_Result::Success(value);
 }
 
-CJS_Result CJX_Form::remerge(CFX_V8* runtime,
+CJS_Result CJX_Form::remerge(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  GetDocument()->DoDataRemerge(true);
+  GetDocument()->DoDataRemerge();
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Form::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -78,7 +80,7 @@
 }
 
 CJS_Result CJX_Form::recalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_EventParam* pEventParam =
       GetDocument()->GetScriptContext()->GetEventParam();
@@ -100,7 +102,7 @@
 }
 
 CJS_Result CJX_Form::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -113,7 +115,7 @@
 }
 
 CJS_Result CJX_Form::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 0)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -128,15 +130,20 @@
       runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
-void CJX_Form::checksumS(CFXJSE_Value* pValue,
+void CJX_Form::checksumS(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetAttribute(XFA_Attribute::Checksum, pValue->ToWideString().AsStringView(),
-                 false);
+    SetAttributeByEnum(XFA_Attribute::Checksum,
+                       fxv8::ReentrantToWideStringHelper(pIsolate, *pValue),
+                       false);
     return;
   }
 
-  Optional<WideString> checksum = TryAttribute(XFA_Attribute::Checksum, false);
-  pValue->SetString(checksum ? checksum->ToUTF8().AsStringView() : "");
+  absl::optional<WideString> checksum =
+      TryAttribute(XFA_Attribute::Checksum, false);
+  *pValue = fxv8::NewStringHelper(
+      pIsolate,
+      checksum.has_value() ? checksum.value().ToUTF8().AsStringView() : "");
 }
diff --git a/fxjs/xfa/cjx_form.h b/fxjs/xfa/cjx_form.h
index c7b1ce3..05ea04c 100644
--- a/fxjs/xfa/cjx_form.h
+++ b/fxjs/xfa/cjx_form.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Form final : public CJX_Model {
  public:
-  explicit CJX_Form(CXFA_Form* form);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Form() override;
 
   // CJX_Object:
@@ -30,6 +30,8 @@
   JSE_PROP(checksumS);
 
  private:
+  explicit CJX_Form(CXFA_Form* form);
+
   using Type__ = CJX_Form;
   using ParentType__ = CJX_Model;
 
diff --git a/fxjs/xfa/cjx_handler.cpp b/fxjs/xfa/cjx_handler.cpp
index 21be88c..d8caeca 100644
--- a/fxjs/xfa/cjx_handler.cpp
+++ b/fxjs/xfa/cjx_handler.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,6 +16,7 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Handler::version(CFXJSE_Value* pValue,
+void CJX_Handler::version(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_handler.h b/fxjs/xfa/cjx_handler.h
index 348eaf1..e5cd2cb 100644
--- a/fxjs/xfa/cjx_handler.h
+++ b/fxjs/xfa/cjx_handler.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Handler final : public CJX_TextNode {
  public:
-  explicit CJX_Handler(CXFA_Handler* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Handler() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(version);
 
  private:
+  explicit CJX_Handler(CXFA_Handler* node);
+
   using Type__ = CJX_Handler;
   using ParentType__ = CJX_TextNode;
 
diff --git a/fxjs/xfa/cjx_hostpseudomodel.cpp b/fxjs/xfa/cjx_hostpseudomodel.cpp
index c45495c..597db30 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.cpp
+++ b/fxjs/xfa/cjx_hostpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,34 +6,33 @@
 
 #include "fxjs/xfa/cjx_hostpseudomodel.h"
 
-#include <memory>
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/check.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
 namespace {
 
-int32_t FilterName(WideStringView wsExpression,
-                   int32_t nStart,
-                   WideString& wsFilter) {
-  ASSERT(nStart > -1);
-  int32_t iLength = wsExpression.GetLength();
-  if (nStart >= iLength)
-    return iLength;
+size_t FilterName(WideStringView wsExpression,
+                  size_t nStart,
+                  WideString& wsFilter) {
+  const size_t nLength = wsExpression.GetLength();
+  if (nStart >= nLength)
+    return nLength;
 
-  int32_t nCount = 0;
+  size_t nCount = 0;
   {
     // Span's lifetime must end before ReleaseBuffer() below.
-    pdfium::span<wchar_t> pBuf = wsFilter.GetBuffer(iLength - nStart);
+    pdfium::span<wchar_t> pBuf = wsFilter.GetBuffer(nLength - nStart);
     const wchar_t* pSrc = wsExpression.unterminated_c_str();
-    while (nStart < iLength) {
+    while (nStart < nLength) {
       wchar_t wCur = pSrc[nStart++];
       if (wCur == ',')
         break;
@@ -70,13 +69,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_HostPseudoModel::~CJX_HostPseudoModel() {}
+CJX_HostPseudoModel::~CJX_HostPseudoModel() = default;
 
 bool CJX_HostPseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_HostPseudoModel::appType(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::appType(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
@@ -84,44 +84,47 @@
     return;
 
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString("Exchange");
+  *pValue = fxv8::NewStringHelper(pIsolate, "Exchange");
 }
 
-void CJX_HostPseudoModel::calculationsEnabled(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::calculationsEnabled(v8::Isolate* pIsolate,
+                                              v8::Local<v8::Value>* pValue,
                                               bool bSetting,
                                               XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    hDoc->GetDocEnvironment()->SetCalculationsEnabled(hDoc,
-                                                      pValue->ToBoolean());
+    hDoc->SetCalculationsEnabled(
+        fxv8::ReentrantToBooleanHelper(pIsolate, *pValue));
     return;
   }
-  pValue->SetBoolean(hDoc->GetDocEnvironment()->IsCalculationsEnabled(hDoc));
+  *pValue = fxv8::NewBooleanHelper(pIsolate, hDoc->IsCalculationsEnabled());
 }
 
-void CJX_HostPseudoModel::currentPage(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::currentPage(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Value>* pValue,
                                       bool bSetting,
                                       XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, pValue->ToInteger());
+    hDoc->SetCurrentPage(fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
     return;
   }
-  pValue->SetInteger(hDoc->GetDocEnvironment()->GetCurrentPage(hDoc));
+  *pValue = fxv8::NewNumberHelper(pIsolate, hDoc->GetCurrentPage());
 }
 
-void CJX_HostPseudoModel::language(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::language(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
@@ -129,29 +132,33 @@
     return;
 
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set language value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set language value."));
     return;
   }
-  pValue->SetString(
-      pNotify->GetAppProvider()->GetLanguage().ToUTF8().AsStringView());
+  ByteString lang = pNotify->GetAppProvider()->GetLanguage().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, lang.AsStringView());
 }
 
-void CJX_HostPseudoModel::numPages(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::numPages(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set numPages value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set numPages value."));
     return;
   }
-  pValue->SetInteger(hDoc->GetDocEnvironment()->CountPages(hDoc));
+  *pValue = fxv8::NewNumberHelper(pIsolate, hDoc->CountPages());
 }
 
-void CJX_HostPseudoModel::platform(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::platform(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
@@ -159,14 +166,16 @@
     return;
 
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set platform value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set platform value."));
     return;
   }
-  pValue->SetString(
-      pNotify->GetAppProvider()->GetPlatform().ToUTF8().AsStringView());
+  ByteString plat = pNotify->GetAppProvider()->GetPlatform().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, plat.AsStringView());
 }
 
-void CJX_HostPseudoModel::title(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::title(v8::Isolate* pIsolate,
+                                v8::Local<v8::Value>* pValue,
                                 bool bSetting,
                                 XFA_Attribute eAttribute) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
@@ -176,58 +185,63 @@
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    hDoc->GetDocEnvironment()->SetTitle(hDoc, pValue->ToWideString());
+    hDoc->SetTitle(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     return;
   }
 
-  WideString wsTitle;
-  hDoc->GetDocEnvironment()->GetTitle(hDoc, wsTitle);
-  pValue->SetString(wsTitle.ToUTF8().AsStringView());
+  ByteString bsTitle = hDoc->GetTitle().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, bsTitle.AsStringView());
 }
 
-void CJX_HostPseudoModel::validationsEnabled(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::validationsEnabled(v8::Isolate* pIsolate,
+                                             v8::Local<v8::Value>* pValue,
                                              bool bSetting,
                                              XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
   if (bSetting) {
-    hDoc->GetDocEnvironment()->SetValidationsEnabled(hDoc, pValue->ToBoolean());
+    hDoc->SetValidationsEnabled(
+        fxv8::ReentrantToBooleanHelper(pIsolate, *pValue));
     return;
   }
 
-  bool bEnabled = hDoc->GetDocEnvironment()->IsValidationsEnabled(hDoc);
-  pValue->SetBoolean(bEnabled);
+  *pValue = fxv8::NewBooleanHelper(pIsolate, hDoc->IsValidationsEnabled());
 }
 
-void CJX_HostPseudoModel::variation(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::variation(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return;
 
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set variation value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set variation value."));
     return;
   }
-  pValue->SetString("Full");
+  *pValue = fxv8::NewStringHelper(pIsolate, "Full");
 }
 
-void CJX_HostPseudoModel::version(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::version(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set version value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set version value."));
     return;
   }
-  pValue->SetString("11");
+  *pValue = fxv8::NewStringHelper(pIsolate, "11");
 }
 
-void CJX_HostPseudoModel::name(CFXJSE_Value* pValue,
+void CJX_HostPseudoModel::name(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value>* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
@@ -235,15 +249,15 @@
     return;
 
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString(
-      pNotify->GetAppProvider()->GetAppName().ToUTF8().AsStringView());
+  ByteString bsName = pNotify->GetAppProvider()->GetAppName().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, bsName.AsStringView());
 }
 
 CJS_Result CJX_HostPseudoModel::gotoURL(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return CJS_Result::Success();
@@ -255,14 +269,12 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  WideString URL = runtime->ToWideString(params[0]);
-  hDoc->GetDocEnvironment()->GotoURL(hDoc, URL);
+  pNotify->GetFFDoc()->GotoURL(runtime->ToWideString(params[0]));
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_HostPseudoModel::openList(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return CJS_Result::Success();
@@ -276,26 +288,25 @@
 
   CXFA_Node* pNode = nullptr;
   if (params[0]->IsObject()) {
-    pNode =
-        ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+    pNode = ToNode(runtime->ToXFAObject(params[0]));
   } else if (params[0]->IsString()) {
     CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
     CXFA_Object* pObject = pScriptContext->GetThisObject();
     if (!pObject)
       return CJS_Result::Success();
 
-    uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
-                      XFA_RESOLVENODE_Siblings;
-    XFA_RESOLVENODE_RS resolveNodeRS;
-    bool bRet = pScriptContext->ResolveObjects(
-        pObject, runtime->ToWideString(params[0]).AsStringView(),
-        &resolveNodeRS, dwFlag, nullptr);
-    if (!bRet || !resolveNodeRS.objects.front()->IsNode())
+    constexpr Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kChildren,
+                                              XFA_ResolveFlag::kParent,
+                                              XFA_ResolveFlag::kSiblings};
+    absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+        pScriptContext->ResolveObjects(
+            pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags);
+    if (!maybeResult.has_value() ||
+        !maybeResult.value().objects.front()->IsNode()) {
       return CJS_Result::Success();
-
-    pNode = resolveNodeRS.objects.front()->AsNode();
+    }
+    pNode = maybeResult.value().objects.front()->AsNode();
   }
-
   if (pNode)
     pNotify->OpenDropDownList(pNode);
 
@@ -303,7 +314,7 @@
 }
 
 CJS_Result CJX_HostPseudoModel::response(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 4)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -335,13 +346,13 @@
 }
 
 CJS_Result CJX_HostPseudoModel::documentInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success(runtime->NewNumber(0));
 }
 
 CJS_Result CJX_HostPseudoModel::resetData(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() > 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -359,26 +370,27 @@
     return CJS_Result::Success();
   }
 
-  int32_t iStart = 0;
   WideString wsName;
   CXFA_Node* pNode = nullptr;
-  int32_t iExpLength = expression.GetLength();
-  while (iStart < iExpLength) {
-    iStart = FilterName(expression.AsStringView(), iStart, wsName);
+  size_t nStart = 0;
+  const size_t nExpLength = expression.GetLength();
+  while (nStart < nExpLength) {
+    nStart = FilterName(expression.AsStringView(), nStart, wsName);
     CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
     CXFA_Object* pObject = pScriptContext->GetThisObject();
     if (!pObject)
       return CJS_Result::Success();
 
-    uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
-                      XFA_RESOLVENODE_Siblings;
-    XFA_RESOLVENODE_RS resolveNodeRS;
-    bool bRet = pScriptContext->ResolveObjects(pObject, wsName.AsStringView(),
-                                               &resolveNodeRS, dwFlag, nullptr);
-    if (!bRet || !resolveNodeRS.objects.front()->IsNode())
+    constexpr Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kChildren,
+                                              XFA_ResolveFlag::kParent,
+                                              XFA_ResolveFlag::kSiblings};
+    absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+        pScriptContext->ResolveObjects(pObject, wsName.AsStringView(), kFlags);
+    if (!maybeResult.has_value() ||
+        !maybeResult.value().objects.front()->IsNode())
       continue;
 
-    pNode = resolveNodeRS.objects.front()->AsNode();
+    pNode = maybeResult.value().objects.front()->AsNode();
     pNotify->ResetData(pNode->IsWidgetReady() ? pNode : nullptr);
   }
   if (!pNode)
@@ -388,7 +400,7 @@
 }
 
 CJS_Result CJX_HostPseudoModel::beep(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return CJS_Result::Success();
@@ -409,7 +421,7 @@
 }
 
 CJS_Result CJX_HostPseudoModel::setFocus(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return CJS_Result::Success();
@@ -424,24 +436,24 @@
   CXFA_Node* pNode = nullptr;
   if (params.size() >= 1) {
     if (params[0]->IsObject()) {
-      pNode =
-          ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+      pNode = ToNode(runtime->ToXFAObject(params[0]));
     } else if (params[0]->IsString()) {
       CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
       CXFA_Object* pObject = pScriptContext->GetThisObject();
       if (!pObject)
         return CJS_Result::Success();
 
-      uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
-                        XFA_RESOLVENODE_Siblings;
-      XFA_RESOLVENODE_RS resolveNodeRS;
-      bool bRet = pScriptContext->ResolveObjects(
-          pObject, runtime->ToWideString(params[0]).AsStringView(),
-          &resolveNodeRS, dwFlag, nullptr);
-      if (!bRet || !resolveNodeRS.objects.front()->IsNode())
+      constexpr Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kChildren,
+                                                XFA_ResolveFlag::kParent,
+                                                XFA_ResolveFlag::kSiblings};
+      absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+          pScriptContext->ResolveObjects(
+              pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags);
+      if (!maybeResult.has_value() ||
+          !maybeResult.value().objects.front()->IsNode()) {
         return CJS_Result::Success();
-
-      pNode = resolveNodeRS.objects.front()->AsNode();
+      }
+      pNode = maybeResult.value().objects.front()->AsNode();
     }
   }
   pNotify->SetFocusWidgetNode(pNode);
@@ -449,7 +461,7 @@
 }
 
 CJS_Result CJX_HostPseudoModel::getFocus(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
@@ -459,15 +471,14 @@
   if (!pNode)
     return CJS_Result::Success();
 
-  CFXJSE_Value* value =
+  v8::Local<v8::Value> value =
       GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
 
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(value);
 }
 
 CJS_Result CJX_HostPseudoModel::messageBox(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return CJS_Result::Success();
@@ -507,13 +518,13 @@
 }
 
 CJS_Result CJX_HostPseudoModel::documentCountInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success(runtime->NewNumber(0));
 }
 
 CJS_Result CJX_HostPseudoModel::print(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!GetDocument()->GetScriptContext()->IsRunAtClient())
     return CJS_Result::Success();
@@ -525,30 +536,28 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  uint32_t dwOptions = 0;
+  Mask<XFA_PrintOpt> dwOptions;
   if (runtime->ToBoolean(params[0]))
-    dwOptions |= XFA_PRINTOPT_ShowDialog;
+    dwOptions |= XFA_PrintOpt::kShowDialog;
   if (runtime->ToBoolean(params[3]))
-    dwOptions |= XFA_PRINTOPT_CanCancel;
+    dwOptions |= XFA_PrintOpt::kCanCancel;
   if (runtime->ToBoolean(params[4]))
-    dwOptions |= XFA_PRINTOPT_ShrinkPage;
+    dwOptions |= XFA_PrintOpt::kShrinkPage;
   if (runtime->ToBoolean(params[5]))
-    dwOptions |= XFA_PRINTOPT_AsImage;
+    dwOptions |= XFA_PrintOpt::kAsImage;
   if (runtime->ToBoolean(params[6]))
-    dwOptions |= XFA_PRINTOPT_ReverseOrder;
+    dwOptions |= XFA_PrintOpt::kReverseOrder;
   if (runtime->ToBoolean(params[7]))
-    dwOptions |= XFA_PRINTOPT_PrintAnnot;
+    dwOptions |= XFA_PrintOpt::kPrintAnnot;
 
   int32_t nStartPage = runtime->ToInt32(params[1]);
   int32_t nEndPage = runtime->ToInt32(params[2]);
-
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  hDoc->GetDocEnvironment()->Print(hDoc, nStartPage, nEndPage, dwOptions);
+  pNotify->GetFFDoc()->Print(nStartPage, nEndPage, dwOptions);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_HostPseudoModel::importData(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -557,7 +566,7 @@
 }
 
 CJS_Result CJX_HostPseudoModel::exportData(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -574,39 +583,36 @@
   if (params.size() >= 2)
     XDP = runtime->ToBoolean(params[1]);
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  hDoc->GetDocEnvironment()->ExportData(hDoc, filePath, XDP);
+  pNotify->GetFFDoc()->ExportData(filePath, XDP);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_HostPseudoModel::pageUp(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  int32_t nCurPage = hDoc->GetDocEnvironment()->GetCurrentPage(hDoc);
-  int32_t nNewPage = 0;
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  int32_t nCurPage = hDoc->GetCurrentPage();
   if (nCurPage <= 1)
     return CJS_Result::Success();
 
-  nNewPage = nCurPage - 1;
-  hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
+  hDoc->SetCurrentPage(nCurPage - 1);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_HostPseudoModel::pageDown(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
-  int32_t nCurPage = hDoc->GetDocEnvironment()->GetCurrentPage(hDoc);
-  int32_t nPageCount = hDoc->GetDocEnvironment()->CountPages(hDoc);
+  CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
+  int32_t nCurPage = hDoc->GetCurrentPage();
+  int32_t nPageCount = hDoc->CountPages();
   if (!nPageCount || nCurPage == nPageCount)
     return CJS_Result::Success();
 
@@ -616,6 +622,6 @@
   else
     nNewPage = nCurPage + 1;
 
-  hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
+  hDoc->SetCurrentPage(nNewPage);
   return CJS_Result::Success();
 }
diff --git a/fxjs/xfa/cjx_hostpseudomodel.h b/fxjs/xfa/cjx_hostpseudomodel.h
index cdc293d..7c1b3b5 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.h
+++ b/fxjs/xfa/cjx_hostpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,12 +11,11 @@
 #include "fxjs/xfa/jse_define.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
-class CFXJSE_Value;
 class CScript_HostPseudoModel;
 
 class CJX_HostPseudoModel final : public CJX_Object {
  public:
-  explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_HostPseudoModel() override;
 
   // CJX_Object:
@@ -54,6 +53,8 @@
   JSE_PROP(name);
 
  private:
+  explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model);
+
   using Type__ = CJX_HostPseudoModel;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp b/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp
index 1d42898..86e0902 100644
--- a/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp
+++ b/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/xfa/cjx_instancemanager.cpp b/fxjs/xfa/cjx_instancemanager.cpp
index 160c4a5..18faf93 100644
--- a/fxjs/xfa/cjx_instancemanager.cpp
+++ b/fxjs/xfa/cjx_instancemanager.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,14 +9,18 @@
 #include <algorithm>
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "third_party/base/notreached.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_instancemanager.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
+#include "xfa/fxfa/parser/cxfa_subform.h"
 
 const CJX_MethodSpec CJX_InstanceManager::MethodSpecs[] = {
     {"addInstance", addInstance_static},
@@ -30,23 +34,24 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_InstanceManager::~CJX_InstanceManager() {}
+CJX_InstanceManager::~CJX_InstanceManager() = default;
 
 bool CJX_InstanceManager::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-int32_t CJX_InstanceManager::SetInstances(int32_t iDesired) {
+int32_t CJX_InstanceManager::SetInstances(v8::Isolate* pIsolate,
+                                          int32_t iDesired) {
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
   if (iDesired < iMin) {
-    ThrowTooManyOccurancesException(L"min");
+    ThrowTooManyOccurrencesException(pIsolate, L"min");
     return 1;
   }
 
   int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
   if (iMax >= 0 && iDesired > iMax) {
-    ThrowTooManyOccurancesException(L"max");
+    ThrowTooManyOccurrencesException(pIsolate, L"max");
     return 2;
   }
 
@@ -61,13 +66,13 @@
             ? wsInstManagerName
             : wsInstManagerName.Last(wsInstManagerName.GetLength() - 1));
     uint32_t dInstanceNameHash =
-        FX_HashCode_GetW(wsInstanceName.AsStringView(), false);
+        FX_HashCode_GetW(wsInstanceName.AsStringView());
     CXFA_Node* pPrevSibling = iDesired == 0
                                   ? GetXFANode()
                                   : GetXFANode()->GetItemIfExists(iDesired - 1);
     if (!pPrevSibling) {
       // TODO(dsinclair): Better error?
-      ThrowIndexOutOfBoundsException();
+      ThrowIndexOutOfBoundsException(pIsolate);
       return 0;
     }
 
@@ -102,15 +107,16 @@
       pNotify->RunNodeInitialize(pNewInstance);
     }
   }
-  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   return 0;
 }
 
-int32_t CJX_InstanceManager::MoveInstance(int32_t iTo, int32_t iFrom) {
+int32_t CJX_InstanceManager::MoveInstance(v8::Isolate* pIsolate,
+                                          int32_t iTo,
+                                          int32_t iFrom) {
   int32_t iCount = GetXFANode()->GetCount();
   if (iFrom > iCount || iTo > iCount - 1) {
-    ThrowIndexOutOfBoundsException();
+    ThrowIndexOutOfBoundsException(pIsolate);
     return 1;
   }
   if (iFrom < 0 || iTo < 0 || iFrom == iTo)
@@ -118,21 +124,20 @@
 
   CXFA_Node* pMoveInstance = GetXFANode()->GetItemIfExists(iFrom);
   if (!pMoveInstance) {
-    ThrowIndexOutOfBoundsException();
+    ThrowIndexOutOfBoundsException(pIsolate);
     return 1;
   }
 
   GetXFANode()->RemoveItem(pMoveInstance, false);
   GetXFANode()->InsertItem(pMoveInstance, iTo, iCount - 1, true);
-  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   return 0;
 }
 
 CJS_Result CJX_InstanceManager::moveInstance(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
@@ -141,29 +146,28 @@
 
   int32_t iFrom = runtime->ToInt32(params[0]);
   int32_t iTo = runtime->ToInt32(params[1]);
-  MoveInstance(iTo, iFrom);
+  MoveInstance(runtime->GetIsolate(), iTo, iFrom);
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return CJS_Result::Success();
 
-  CXFA_Node* pToInstance = GetXFANode()->GetItemIfExists(iTo);
-  if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform)
+  CXFA_Node* pXFA = GetXFANode();
+  auto* pToInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iTo));
+  if (pToInstance)
     pNotify->RunSubformIndexChange(pToInstance);
 
-  CXFA_Node* pFromInstance = GetXFANode()->GetItemIfExists(iFrom);
-  if (pFromInstance &&
-      pFromInstance->GetElementType() == XFA_Element::Subform) {
+  auto* pFromInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iFrom));
+  if (pFromInstance)
     pNotify->RunSubformIndexChange(pFromInstance);
-  }
 
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_InstanceManager::removeInstance(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
@@ -178,7 +182,7 @@
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
   if (iCount - 1 < iMin)
-    return CJS_Result::Failure(JSMessage::kTooManyOccurances);
+    return CJS_Result::Failure(JSMessage::kTooManyOccurrences);
 
   CXFA_Node* pRemoveInstance = GetXFANode()->GetItemIfExists(iIndex);
   if (!pRemoveInstance)
@@ -188,37 +192,35 @@
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
+    CXFA_Node* pXFA = GetXFANode();
     for (int32_t i = iIndex; i < iCount - 1; i++) {
-      CXFA_Node* pSubformInstance = GetXFANode()->GetItemIfExists(i);
-      if (pSubformInstance &&
-          pSubformInstance->GetElementType() == XFA_Element::Subform) {
+      auto* pSubformInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(i));
+      if (pSubformInstance)
         pNotify->RunSubformIndexChange(pSubformInstance);
-      }
     }
   }
-  GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_InstanceManager::setInstances(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  SetInstances(runtime->ToInt32(params[0]));
+  SetInstances(runtime->GetIsolate(), runtime->ToInt32(params[0]));
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_InstanceManager::addInstance(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
@@ -233,7 +235,7 @@
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
   if (iMax >= 0 && iCount >= iMax)
-    return CJS_Result::Failure(JSMessage::kTooManyOccurances);
+    return CJS_Result::Failure(JSMessage::kTooManyOccurrences);
 
   CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(fFlags);
   if (!pNewInstance)
@@ -244,22 +246,18 @@
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
     pNotify->RunNodeInitialize(pNewInstance);
-    GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+    GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   }
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pNewInstance);
-
   return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pNewInstance));
 }
 
 CJS_Result CJX_InstanceManager::insertInstance(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_Document* doc = static_cast<CFXJSE_Engine*>(runtime)->GetDocument();
+  CXFA_Document* doc = runtime->GetDocument();
   if (doc->GetFormType() != FormType::kXFAFull)
     return CJS_Result::Failure(JSMessage::kNotSupportedError);
 
@@ -289,46 +287,47 @@
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (pNotify) {
     pNotify->RunNodeInitialize(pNewInstance);
-    GetDocument()->GetLayoutProcessor()->AddChangedContainer(
-        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+    GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
   }
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pNewInstance);
-
   return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pNewInstance));
 }
 
-void CJX_InstanceManager::max(CFXJSE_Value* pValue,
+void CJX_InstanceManager::max(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
-  pValue->SetInteger(occur ? occur->GetMax() : CXFA_Occur::kDefaultMax);
+  *pValue = fxv8::NewNumberHelper(
+      pIsolate, occur ? occur->GetMax() : CXFA_Occur::kDefaultMax);
 }
 
-void CJX_InstanceManager::min(CFXJSE_Value* pValue,
+void CJX_InstanceManager::min(v8::Isolate* pIsolate,
+                              v8::Local<v8::Value>* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
-  pValue->SetInteger(occur ? occur->GetMin() : CXFA_Occur::kDefaultMin);
+  *pValue = fxv8::NewNumberHelper(
+      pIsolate, occur ? occur->GetMin() : CXFA_Occur::kDefaultMin);
 }
 
-void CJX_InstanceManager::count(CFXJSE_Value* pValue,
+void CJX_InstanceManager::count(v8::Isolate* pIsolate,
+                                v8::Local<v8::Value>* pValue,
                                 bool bSetting,
                                 XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetInstances(pValue->ToInteger());
+    SetInstances(pIsolate, fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
     return;
   }
-  pValue->SetInteger(GetXFANode()->GetCount());
+  *pValue = fxv8::NewNumberHelper(pIsolate, GetXFANode()->GetCount());
 }
diff --git a/fxjs/xfa/cjx_instancemanager.h b/fxjs/xfa/cjx_instancemanager.h
index 3fae9ab..96210e8 100644
--- a/fxjs/xfa/cjx_instancemanager.h
+++ b/fxjs/xfa/cjx_instancemanager.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,12 +9,13 @@
 
 #include "fxjs/xfa/cjx_node.h"
 #include "fxjs/xfa/jse_define.h"
+#include "v8/include/v8-forward.h"
 
 class CXFA_InstanceManager;
 
 class CJX_InstanceManager final : public CJX_Node {
  public:
-  explicit CJX_InstanceManager(CXFA_InstanceManager* mgr);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_InstanceManager() override;
 
   // CJX_Object:
@@ -30,16 +31,18 @@
   JSE_PROP(max);
   JSE_PROP(min);
 
-  int32_t MoveInstance(int32_t iTo, int32_t iFrom);
+  int32_t MoveInstance(v8::Isolate* pIsolate, int32_t iTo, int32_t iFrom);
 
  private:
+  explicit CJX_InstanceManager(CXFA_InstanceManager* mgr);
+
   using Type__ = CJX_InstanceManager;
   using ParentType__ = CJX_Node;
 
   static const TypeTag static_type__ = TypeTag::InstanceManager;
   static const CJX_MethodSpec MethodSpecs[];
 
-  int32_t SetInstances(int32_t iDesired);
+  int32_t SetInstances(v8::Isolate* pIsolate, int32_t iDesired);
 };
 
 #endif  // FXJS_XFA_CJX_INSTANCEMANAGER_H_
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.cpp b/fxjs/xfa/cjx_layoutpseudomodel.cpp
index 35852a8..484d883 100644
--- a/fxjs/xfa/cjx_layoutpseudomodel.cpp
+++ b/fxjs/xfa/cjx_layoutpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,12 +10,13 @@
 #include <utility>
 
 #include "core/fxcrt/fx_coordinates.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
 #include "xfa/fxfa/layout/cxfa_layoutitem.h"
@@ -56,36 +57,39 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {}
+CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() = default;
 
 bool CJX_LayoutPseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_LayoutPseudoModel::ready(CFXJSE_Value* pValue,
+void CJX_LayoutPseudoModel::ready(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
   if (bSetting) {
-    ThrowException(WideString::FromASCII("Unable to set ready value."));
+    ThrowException(pIsolate,
+                   WideString::FromASCII("Unable to set ready value."));
     return;
   }
 
-  int32_t iStatus = pNotify->GetLayoutStatus();
-  pValue->SetBoolean(iStatus >= 2);
+  CXFA_FFDocView::LayoutStatus iStatus = pNotify->GetLayoutStatus();
+  const bool bReady = iStatus != CXFA_FFDocView::LayoutStatus::kNone &&
+                      iStatus != CXFA_FFDocView::LayoutStatus::kStart;
+  *pValue = fxv8::NewBooleanHelper(pIsolate, bReady);
 }
 
-CJS_Result CJX_LayoutPseudoModel::HWXY(
-    CFX_V8* runtime,
+CJS_Result CJX_LayoutPseudoModel::DoHWXYInternal(
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params,
-    XFA_LAYOUTMODEL_HWXY layoutModel) {
+    HWXY layoutModel) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Success();
 
@@ -111,18 +115,18 @@
     return CJS_Result::Success(runtime->NewNumber(0.0));
 
   CXFA_Measurement measure;
-  CFX_RectF rtRect = pLayoutItem->GetRect(true);
+  CFX_RectF rtRect = pLayoutItem->GetRelativeRect();
   switch (layoutModel) {
-    case XFA_LAYOUTMODEL_H:
+    case HWXY::kH:
       measure.Set(rtRect.height, XFA_Unit::Pt);
       break;
-    case XFA_LAYOUTMODEL_W:
+    case HWXY::kW:
       measure.Set(rtRect.width, XFA_Unit::Pt);
       break;
-    case XFA_LAYOUTMODEL_X:
+    case HWXY::kX:
       measure.Set(rtRect.left, XFA_Unit::Pt);
       break;
-    case XFA_LAYOUTMODEL_Y:
+    case HWXY::kY:
       measure.Set(rtRect.top, XFA_Unit::Pt);
       break;
   }
@@ -137,64 +141,63 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::h(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return HWXY(runtime, params, XFA_LAYOUTMODEL_H);
+  return DoHWXYInternal(runtime, params, HWXY::kH);
 }
 
 CJS_Result CJX_LayoutPseudoModel::w(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return HWXY(runtime, params, XFA_LAYOUTMODEL_W);
+  return DoHWXYInternal(runtime, params, HWXY::kW);
 }
 
 CJS_Result CJX_LayoutPseudoModel::x(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return HWXY(runtime, params, XFA_LAYOUTMODEL_X);
+  return DoHWXYInternal(runtime, params, HWXY::kX);
 }
 
 CJS_Result CJX_LayoutPseudoModel::y(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return HWXY(runtime, params, XFA_LAYOUTMODEL_Y);
+  return DoHWXYInternal(runtime, params, HWXY::kY);
 }
 
-CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFX_V8* runtime,
-                                                    bool bNumbered) {
+CJS_Result CJX_LayoutPseudoModel::AllPageCount(CFXJSE_Engine* runtime) {
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
+  return CJS_Result::Success(runtime->NewNumber(pDocLayout->CountPages()));
+}
+
+CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFXJSE_Engine* runtime) {
   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
   int32_t iPageCount = 0;
   int32_t iPageNum = pDocLayout->CountPages();
-  if (bNumbered) {
-    for (int32_t i = 0; i < iPageNum; i++) {
-      CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
-      if (!pLayoutPage)
-        continue;
+  for (int32_t i = 0; i < iPageNum; i++) {
+    CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
+    if (!pLayoutPage)
+      continue;
 
-      CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
-      if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered))
-        iPageCount++;
-    }
-  } else {
-    iPageCount = iPageNum;
+    CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
+    if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered))
+      iPageCount++;
   }
   return CJS_Result::Success(runtime->NewNumber(iPageCount));
 }
 
 CJS_Result CJX_LayoutPseudoModel::pageCount(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return NumberedPageCount(runtime, true);
+  return NumberedPageCount(runtime);
 }
 
 CJS_Result CJX_LayoutPseudoModel::pageSpan(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Success();
 
@@ -211,7 +214,7 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::page(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, false);
 }
@@ -261,7 +264,7 @@
                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
               continue;
             }
-            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
+            if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
               continue;
 
             formItems.insert(pItemChild->GetFormNode());
@@ -282,7 +285,7 @@
                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
               continue;
             }
-            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
+            if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
               continue;
 
             formItems.insert(pItemChild->GetFormNode());
@@ -317,7 +320,7 @@
               continue;
             if (pItemChild->GetFormNode()->GetElementType() != eType)
               continue;
-            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
+            if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
               continue;
 
             formItems.insert(pItemChild->GetFormNode());
@@ -334,7 +337,7 @@
               continue;
             if (pItemChild->GetFormNode()->GetElementType() != eType)
               continue;
-            if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode()))
+            if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
               continue;
 
             formItems.insert(pItemChild->GetFormNode());
@@ -348,7 +351,7 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::pageContent(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -369,59 +372,57 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
-  auto pArrayNodeList = pdfium::MakeUnique<CXFA_ArrayNodeList>(GetDocument());
+  CXFA_Document* pDoc = GetDocument();
+  auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDoc);
+  auto* pArrayNodeList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc);
+  pDoc->GetNodeOwner()->PersistList(pArrayNodeList);
   pArrayNodeList->SetArrayNodeList(
       GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea));
-
-  // TODO(dsinclair): Who owns the array once we release it? Won't this leak?
-  return CJS_Result::Success(static_cast<CFXJSE_Engine*>(runtime)->NewXFAObject(
-      pArrayNodeList.release(),
-      GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
+  return CJS_Result::Success(runtime->NewNormalXFAObject(pArrayNodeList));
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPageCount(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return NumberedPageCount(runtime, false);
+  return AllPageCount(runtime);
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPageCountInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success(runtime->NewNumber(0));
 }
 
 CJS_Result CJX_LayoutPseudoModel::sheetCountInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success(runtime->NewNumber(0));
 }
 
 CJS_Result CJX_LayoutPseudoModel::relayout(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   CXFA_Node* pRootNode = GetDocument()->GetRoot();
   auto* pLayoutProcessor = GetDocument()->GetLayoutProcessor();
   CXFA_Form* pFormRoot =
       pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
   if (pFormRoot) {
-    CXFA_Node* pContentRootNode = pFormRoot->GetFirstChild();
-    if (pContentRootNode)
-      pLayoutProcessor->AddChangedContainer(pContentRootNode);
+    if (pFormRoot->GetFirstChild())
+      pLayoutProcessor->SetHasChangedContainer();
   }
-  pLayoutProcessor->SetForceRelayout(true);
+  pLayoutProcessor->SetForceRelayout();
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPageSpan(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return pageSpan(runtime, params);
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPageInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -430,7 +431,7 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::sheetInBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -439,38 +440,37 @@
 }
 
 CJS_Result CJX_LayoutPseudoModel::sheet(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, true);
 }
 
 CJS_Result CJX_LayoutPseudoModel::relayoutPageArea(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LayoutPseudoModel::sheetCount(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  return NumberedPageCount(runtime, false);
+  return AllPageCount(runtime);
 }
 
 CJS_Result CJX_LayoutPseudoModel::absPage(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return PageInternals(runtime, params, true);
 }
 
 CJS_Result CJX_LayoutPseudoModel::PageInternals(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params,
     bool bAbsPage) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  CXFA_Node* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Success(runtime->NewNumber(0));
 
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.h b/fxjs/xfa/cjx_layoutpseudomodel.h
index d5f0cba..80cf310 100644
--- a/fxjs/xfa/cjx_layoutpseudomodel.h
+++ b/fxjs/xfa/cjx_layoutpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,21 +12,13 @@
 #include "fxjs/xfa/cjx_object.h"
 #include "fxjs/xfa/jse_define.h"
 
-enum XFA_LAYOUTMODEL_HWXY {
-  XFA_LAYOUTMODEL_H,
-  XFA_LAYOUTMODEL_W,
-  XFA_LAYOUTMODEL_X,
-  XFA_LAYOUTMODEL_Y
-};
-
-class CFXJSE_Value;
 class CScript_LayoutPseudoModel;
 class CXFA_LayoutProcessor;
 class CXFA_Node;
 
 class CJX_LayoutPseudoModel final : public CJX_Object {
  public:
-  explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_LayoutPseudoModel() override;
 
   // CJX_Object:
@@ -55,21 +47,26 @@
   JSE_PROP(ready);
 
  private:
+  enum class HWXY { kH, kW, kX, kY };
+
+  explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model);
+
   using Type__ = CJX_LayoutPseudoModel;
   using ParentType__ = CJX_Object;
 
   static const TypeTag static_type__ = TypeTag::LayoutPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 
-  CJS_Result NumberedPageCount(CFX_V8* runtime, bool bNumbered);
-  CJS_Result HWXY(CFX_V8* runtime,
-                  const std::vector<v8::Local<v8::Value>>& params,
-                  XFA_LAYOUTMODEL_HWXY layoutModel);
+  CJS_Result AllPageCount(CFXJSE_Engine* runtime);
+  CJS_Result NumberedPageCount(CFXJSE_Engine* runtime);
+  CJS_Result DoHWXYInternal(CFXJSE_Engine* runtime,
+                            const std::vector<v8::Local<v8::Value>>& params,
+                            HWXY layoutModel);
   std::vector<CXFA_Node*> GetObjArray(CXFA_LayoutProcessor* pDocLayout,
                                       int32_t iPageNo,
                                       const WideString& wsType,
                                       bool bOnPageArea);
-  CJS_Result PageInternals(CFX_V8* runtime,
+  CJS_Result PageInternals(CFXJSE_Engine* runtime,
                            const std::vector<v8::Local<v8::Value>>& params,
                            bool bAbsPage);
 };
diff --git a/fxjs/xfa/cjx_list.cpp b/fxjs/xfa/cjx_list.cpp
index 7c0cf23..8c4735d 100644
--- a/fxjs/xfa/cjx_list.cpp
+++ b/fxjs/xfa/cjx_list.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,11 +8,13 @@
 
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
 #include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_list.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
@@ -26,7 +28,7 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_List::~CJX_List() {}
+CJX_List::~CJX_List() = default;
 
 bool CJX_List::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
@@ -36,45 +38,43 @@
   return ToList(GetXFAObject());
 }
 
-CJS_Result CJX_List::append(CFX_V8* runtime,
+CJS_Result CJX_List::append(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  auto* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  GetXFAList()->Append(pNode);
+  if (!GetXFAList()->Append(pNode))
+    return CJS_Result::Failure(JSMessage::kWouldBeCyclic);
+
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_List::insert(CFX_V8* runtime,
+CJS_Result CJX_List::insert(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNewNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  auto* pNewNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNewNode)
     return CJS_Result::Failure(JSMessage::kValueError);
 
-  auto* pBeforeNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[1]));
+  auto* pBeforeNode = ToNode(runtime->ToXFAObject(params[1]));
   if (!GetXFAList()->Insert(pNewNode, pBeforeNode))
     return CJS_Result::Failure(JSMessage::kValueError);
 
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_List::remove(CFX_V8* runtime,
+CJS_Result CJX_List::remove(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  auto* pNode =
-      ToNode(static_cast<CFXJSE_Engine*>(runtime)->ToXFAObject(params[0]));
+  auto* pNode = ToNode(runtime->ToXFAObject(params[0]));
   if (!pNode)
     return CJS_Result::Failure(JSMessage::kValueError);
 
@@ -82,7 +82,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_List::item(CFX_V8* runtime,
+CJS_Result CJX_List::item(CFXJSE_Engine* runtime,
                           const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -92,18 +92,18 @@
   if (index < 0 || cast_index >= GetXFAList()->GetLength())
     return CJS_Result::Failure(JSMessage::kInvalidInputError);
 
-  return CJS_Result::Success(static_cast<CFXJSE_Engine*>(runtime)->NewXFAObject(
-      GetXFAList()->Item(cast_index),
-      GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
+  return CJS_Result::Success(
+      runtime->NewNormalXFAObject(GetXFAList()->Item(cast_index)));
 }
 
-void CJX_List::length(CFXJSE_Value* pValue,
+void CJX_List::length(v8::Isolate* pIsolate,
+                      v8::Local<v8::Value>* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetInteger(
-      pdfium::base::checked_cast<int32_t>(GetXFAList()->GetLength()));
+  *pValue = fxv8::NewNumberHelper(
+      pIsolate, pdfium::base::checked_cast<int32_t>(GetXFAList()->GetLength()));
 }
diff --git a/fxjs/xfa/cjx_list.h b/fxjs/xfa/cjx_list.h
index 842b859..4471867 100644
--- a/fxjs/xfa/cjx_list.h
+++ b/fxjs/xfa/cjx_list.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_List : public CJX_Object {
  public:
-  explicit CJX_List(CXFA_List* list);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_List() override;
 
   // CJX_Object:
@@ -27,6 +27,9 @@
 
   JSE_PROP(length);
 
+ protected:
+  explicit CJX_List(CXFA_List* list);
+
  private:
   using Type__ = CJX_List;
   using ParentType__ = CJX_Object;
diff --git a/fxjs/xfa/cjx_list_embeddertest.cpp b/fxjs/xfa/cjx_list_embeddertest.cpp
index 02450dc..17d2de4 100644
--- a/fxjs/xfa/cjx_list_embeddertest.cpp
+++ b/fxjs/xfa/cjx_list_embeddertest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/fxjs/xfa/cjx_logpseudomodel.cpp b/fxjs/xfa/cjx_logpseudomodel.cpp
index 8cb9ee7..a100697 100644
--- a/fxjs/xfa/cjx_logpseudomodel.cpp
+++ b/fxjs/xfa/cjx_logpseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,14 +23,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_LogPseudoModel::~CJX_LogPseudoModel() {}
+CJX_LogPseudoModel::~CJX_LogPseudoModel() = default;
 
 bool CJX_LogPseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_LogPseudoModel::message(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // Uncomment to allow using xfa.log.message(""); from JS.
   // fprintf(stderr, "LOG\n");
@@ -43,25 +43,25 @@
 }
 
 CJS_Result CJX_LogPseudoModel::traceEnabled(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LogPseudoModel::traceActivate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LogPseudoModel::traceDeactivate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_LogPseudoModel::trace(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
diff --git a/fxjs/xfa/cjx_logpseudomodel.h b/fxjs/xfa/cjx_logpseudomodel.h
index fda3bb9..030d1df 100644
--- a/fxjs/xfa/cjx_logpseudomodel.h
+++ b/fxjs/xfa/cjx_logpseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
 // xfa_basic_data_element_script is removed.
 class CJX_LogPseudoModel final : public CJX_Object {
  public:
-  explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_LogPseudoModel() override;
 
   // CJX_Object:
@@ -29,6 +29,8 @@
   JSE_METHOD(trace);
 
  private:
+  explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model);
+
   using Type__ = CJX_LogPseudoModel;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_manifest.cpp b/fxjs/xfa/cjx_manifest.cpp
index f94232e..7d4e6e6 100644
--- a/fxjs/xfa/cjx_manifest.cpp
+++ b/fxjs/xfa/cjx_manifest.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_manifest.h"
 
 const CJX_MethodSpec CJX_Manifest::MethodSpecs[] = {
@@ -20,14 +21,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Manifest::~CJX_Manifest() {}
+CJX_Manifest::~CJX_Manifest() = default;
 
 bool CJX_Manifest::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Manifest::evaluate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_manifest.h b/fxjs/xfa/cjx_manifest.h
index 8380ac6..0f95648 100644
--- a/fxjs/xfa/cjx_manifest.h
+++ b/fxjs/xfa/cjx_manifest.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Manifest final : public CJX_Node {
  public:
-  explicit CJX_Manifest(CXFA_Manifest* manifest);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Manifest() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_METHOD(evaluate);
 
  private:
+  explicit CJX_Manifest(CXFA_Manifest* manifest);
+
   using Type__ = CJX_Manifest;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_model.cpp b/fxjs/xfa/cjx_model.cpp
index 4fa0a2e..21a767d 100644
--- a/fxjs/xfa/cjx_model.cpp
+++ b/fxjs/xfa/cjx_model.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/parser/cxfa_delta.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
@@ -24,20 +25,20 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Model::~CJX_Model() {}
+CJX_Model::~CJX_Model() = default;
 
 bool CJX_Model::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Model::clearErrorList(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Model::createNode(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -60,21 +61,19 @@
     if (!pNewNode->HasAttribute(XFA_Attribute::Name))
       return CJS_Result::Failure(JSMessage::kParamError);
 
-    pNewNode->JSObject()->SetAttribute(XFA_Attribute::Name, name.AsStringView(),
-                                       true);
+    pNewNode->JSObject()->SetAttributeByEnum(XFA_Attribute::Name, name, true);
     if (pNewNode->GetPacketType() == XFA_PacketType::Datasets)
       pNewNode->CreateXMLMappingNode();
   }
 
-  CFXJSE_Value* value =
+  v8::Local<v8::Value> value =
       GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNewNode);
 
-  return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+  return CJS_Result::Success(value);
 }
 
 CJS_Result CJX_Model::isCompatibleNS(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -87,10 +86,12 @@
       runtime->NewBoolean(TryNamespace().value_or(WideString()) == nameSpace));
 }
 
-void CJX_Model::context(CFXJSE_Value* pValue,
+void CJX_Model::context(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {}
 
-void CJX_Model::aliasNode(CFXJSE_Value* pValue,
+void CJX_Model::aliasNode(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_model.h b/fxjs/xfa/cjx_model.h
index 9fd54f5..7b0e17e 100644
--- a/fxjs/xfa/cjx_model.h
+++ b/fxjs/xfa/cjx_model.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Model : public CJX_Node {
  public:
-  explicit CJX_Model(CXFA_Node* obj);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Model() override;
 
   // CJX_Object:
@@ -27,6 +27,9 @@
   JSE_PROP(aliasNode);
   JSE_PROP(context);
 
+ protected:
+  explicit CJX_Model(CXFA_Node* obj);
+
  private:
   using Type__ = CJX_Model;
   using ParentType__ = CJX_Node;
diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp
index 3877f72..0685107 100644
--- a/fxjs/xfa/cjx_node.cpp
+++ b/fxjs/xfa/cjx_node.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,25 +11,27 @@
 #include <vector>
 
 #include "core/fxcrt/cfx_memorystream.h"
+#include "core/fxcrt/cfx_read_only_string_stream.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/xml/cfx_xmldocument.h"
-#include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlparser.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_document_parser.h"
+#include "xfa/fxfa/parser/cxfa_document_builder.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/xfa_basic_data.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
 namespace {
 
-enum class EventAppliesToo : uint8_t {
+enum class EventAppliesTo : uint8_t {
   kNone = 0,
   kAll = 1,
   kAllNonRecursive = 2,
@@ -40,90 +42,47 @@
   kChoiceList = 7
 };
 
-struct XFA_ExecEventParaInfo {
- public:
+struct ExecEventParaInfo {
   uint32_t m_uHash;  // hashed as wide string.
   XFA_EVENTTYPE m_eventType;
-  EventAppliesToo m_validFlags;
+  EventAppliesTo m_validFlags;
 };
 
 #undef PARA
-#define PARA(a, b, c, d) a, c, d
-const XFA_ExecEventParaInfo gs_eventParaInfos[] = {
-    {PARA(0x109d7ce7,
-          "mouseEnter",
-          XFA_EVENT_MouseEnter,
-          EventAppliesToo::kField)},
-    {PARA(0x1bfc72d9,
-          "preOpen",
-          XFA_EVENT_PreOpen,
-          EventAppliesToo::kChoiceList)},
-    {PARA(0x2196a452,
-          "initialize",
-          XFA_EVENT_Initialize,
-          EventAppliesToo::kAll)},
-    {PARA(0x27410f03,
-          "mouseExit",
-          XFA_EVENT_MouseExit,
-          EventAppliesToo::kField)},
-    {PARA(0x36f1c6d8,
-          "preSign",
-          XFA_EVENT_PreSign,
-          EventAppliesToo::kSignature)},
-    {PARA(0x4731d6ba,
-          "exit",
-          XFA_EVENT_Exit,
-          EventAppliesToo::kAllNonRecursive)},
-    {PARA(0x7233018a, "validate", XFA_EVENT_Validate, EventAppliesToo::kAll)},
-    {PARA(0x8808385e,
-          "indexChange",
-          XFA_EVENT_IndexChange,
-          EventAppliesToo::kSubform)},
-    {PARA(0x891f4606,
-          "change",
-          XFA_EVENT_Change,
-          EventAppliesToo::kFieldOrExclusion)},
-    {PARA(0x9f693b21,
-          "mouseDown",
-          XFA_EVENT_MouseDown,
-          EventAppliesToo::kField)},
-    {PARA(0xcdce56b3,
-          "full",
-          XFA_EVENT_Full,
-          EventAppliesToo::kFieldOrExclusion)},
-    {PARA(0xd576d08e, "mouseUp", XFA_EVENT_MouseUp, EventAppliesToo::kField)},
-    {PARA(0xd95657a6,
-          "click",
-          XFA_EVENT_Click,
-          EventAppliesToo::kFieldOrExclusion)},
-    {PARA(0xdbfbe02e, "calculate", XFA_EVENT_Calculate, EventAppliesToo::kAll)},
-    {PARA(0xe25fa7b8,
-          "postOpen",
-          XFA_EVENT_PostOpen,
-          EventAppliesToo::kChoiceList)},
-    {PARA(0xe28dce7e,
-          "enter",
-          XFA_EVENT_Enter,
-          EventAppliesToo::kAllNonRecursive)},
-    {PARA(0xfd54fbb7,
-          "postSign",
-          XFA_EVENT_PostSign,
-          EventAppliesToo::kSignature)},
+#define PARA(a, b, c, d) a, c, EventAppliesTo::d
+const ExecEventParaInfo kExecEventParaInfoTable[] = {
+    {PARA(0x109d7ce7, "mouseEnter", XFA_EVENT_MouseEnter, kField)},
+    {PARA(0x1bfc72d9, "preOpen", XFA_EVENT_PreOpen, kChoiceList)},
+    {PARA(0x2196a452, "initialize", XFA_EVENT_Initialize, kAll)},
+    {PARA(0x27410f03, "mouseExit", XFA_EVENT_MouseExit, kField)},
+    {PARA(0x36f1c6d8, "preSign", XFA_EVENT_PreSign, kSignature)},
+    {PARA(0x4731d6ba, "exit", XFA_EVENT_Exit, kAllNonRecursive)},
+    {PARA(0x7233018a, "validate", XFA_EVENT_Validate, kAll)},
+    {PARA(0x8808385e, "indexChange", XFA_EVENT_IndexChange, kSubform)},
+    {PARA(0x891f4606, "change", XFA_EVENT_Change, kFieldOrExclusion)},
+    {PARA(0x9f693b21, "mouseDown", XFA_EVENT_MouseDown, kField)},
+    {PARA(0xcdce56b3, "full", XFA_EVENT_Full, kFieldOrExclusion)},
+    {PARA(0xd576d08e, "mouseUp", XFA_EVENT_MouseUp, kField)},
+    {PARA(0xd95657a6, "click", XFA_EVENT_Click, kFieldOrExclusion)},
+    {PARA(0xdbfbe02e, "calculate", XFA_EVENT_Calculate, kAll)},
+    {PARA(0xe25fa7b8, "postOpen", XFA_EVENT_PostOpen, kChoiceList)},
+    {PARA(0xe28dce7e, "enter", XFA_EVENT_Enter, kAllNonRecursive)},
+    {PARA(0xfd54fbb7, "postSign", XFA_EVENT_PostSign, kSignature)},
 };
 #undef PARA
 
-const XFA_ExecEventParaInfo* GetEventParaInfoByName(
+const ExecEventParaInfo* GetExecEventParaInfoByName(
     WideStringView wsEventName) {
   if (wsEventName.IsEmpty())
     return nullptr;
 
-  uint32_t uHash = FX_HashCode_GetW(wsEventName, false);
+  uint32_t uHash = FX_HashCode_GetW(wsEventName);
   auto* result = std::lower_bound(
-      std::begin(gs_eventParaInfos), std::end(gs_eventParaInfos), uHash,
-      [](const XFA_ExecEventParaInfo& iter, const uint16_t& hash) {
+      std::begin(kExecEventParaInfoTable), std::end(kExecEventParaInfoTable),
+      uHash, [](const ExecEventParaInfo& iter, const uint16_t& hash) {
         return iter.m_uHash < hash;
       });
-  if (result != std::end(gs_eventParaInfos) && result->m_uHash == uHash)
+  if (result != std::end(kExecEventParaInfoTable) && result->m_uHash == uHash)
     return result;
   return nullptr;
 }
@@ -153,11 +112,7 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CXFA_Node* CJX_Node::GetXFANode() const {
-  return ToNode(GetXFAObject());
-}
-
-CJS_Result CJX_Node::applyXSL(CFX_V8* runtime,
+CJS_Result CJX_Node::applyXSL(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -167,7 +122,7 @@
 }
 
 CJS_Result CJX_Node::assignNode(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -176,33 +131,30 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Node::clone(CFX_V8* runtime,
+CJS_Result CJX_Node::clone(CFXJSE_Engine* runtime,
                            const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_Node* pCloneNode = GetXFANode()->Clone(runtime->ToBoolean(params[0]));
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pCloneNode);
-
   return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pCloneNode));
 }
 
 CJS_Result CJX_Node::getAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString expression = runtime->ToWideString(params[0]);
   return CJS_Result::Success(runtime->NewString(
-      GetAttribute(expression.AsStringView()).ToUTF8().AsStringView()));
+      GetAttributeByString(expression.AsStringView()).ToUTF8().AsStringView()));
 }
 
 CJS_Result CJX_Node::getElement(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -217,21 +169,18 @@
   if (!pNode)
     return CJS_Result::Success(runtime->NewNull());
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
-
   return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode));
 }
 
 CJS_Result CJX_Node::isPropertySpecified(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString expression = runtime->ToWideString(params[0]);
-  Optional<XFA_ATTRIBUTEINFO> attr =
+  absl::optional<XFA_ATTRIBUTEINFO> attr =
       XFA_GetAttributeByName(expression.AsStringView());
   if (attr.has_value() && HasAttribute(attr.value().attribute))
     return CJS_Result::Success(runtime->NewBoolean(true));
@@ -252,7 +201,7 @@
   return CJS_Result::Success(runtime->NewBoolean(bHas));
 }
 
-CJS_Result CJX_Node::loadXML(CFX_V8* runtime,
+CJS_Result CJX_Node::loadXML(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 3)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -265,18 +214,23 @@
   if (params.size() >= 2)
     bIgnoreRoot = runtime->ToBoolean(params[1]);
 
-  bool bOverwrite = 0;
+  bool bOverwrite = false;
   if (params.size() >= 3)
     bOverwrite = runtime->ToBoolean(params[2]);
 
-  auto pParser = pdfium::MakeUnique<CXFA_DocumentParser>(GetDocument());
-  CFX_XMLNode* pXMLNode = pParser->ParseXMLData(expression);
+  auto stream =
+      pdfium::MakeRetain<CFX_ReadOnlyStringStream>(std::move(expression));
+
+  CFX_XMLParser parser(stream);
+  std::unique_ptr<CFX_XMLDocument> xml_doc = parser.Parse();
+  CXFA_DocumentBuilder builder(GetDocument());
+  CFX_XMLNode* pXMLNode = builder.Build(xml_doc.get());
   if (!pXMLNode)
     return CJS_Result::Success();
 
   CFX_XMLDocument* top_xml_doc =
-      GetXFANode()->GetDocument()->GetNotify()->GetHDOC()->GetXMLDocument();
-  top_xml_doc->AppendNodesFrom(pParser->GetXMLDoc().get());
+      GetXFANode()->GetDocument()->GetNotify()->GetFFDoc()->GetXMLDocument();
+  top_xml_doc->AppendNodesFrom(xml_doc.get());
 
   if (bIgnoreRoot &&
       (pXMLNode->GetType() != CFX_XMLNode::Type::kElement ||
@@ -288,7 +242,7 @@
   WideString wsContentType = GetCData(XFA_Attribute::ContentType);
   if (!wsContentType.IsEmpty()) {
     pFakeRoot->JSObject()->SetCData(XFA_Attribute::ContentType,
-                                    WideString(wsContentType), false, false);
+                                    WideString(wsContentType));
   }
 
   CFX_XMLNode* pFakeXMLRoot = pFakeRoot->GetXMLMappingNode();
@@ -317,8 +271,8 @@
     pFakeXMLRoot->AppendLastChild(pXMLNode);
   }
 
-  pParser->ConstructXFANode(pFakeRoot, pFakeXMLRoot);
-  pFakeRoot = pParser->GetRootNode();
+  builder.ConstructXFANode(pFakeRoot, pFakeXMLRoot);
+  pFakeRoot = builder.GetRootNode();
   if (!pFakeRoot)
     return CJS_Result::Success();
 
@@ -330,7 +284,7 @@
       CXFA_Node* pItem = pNewChild->GetNextSibling();
       pFakeRoot->RemoveChildAndNotify(pNewChild, true);
       GetXFANode()->InsertChildAndNotify(index++, pNewChild);
-      pNewChild->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+      pNewChild->SetInitializedFlagAndNotify();
       pNewChild = pItem;
     }
 
@@ -358,7 +312,7 @@
       CXFA_Node* pItem = pChild->GetNextSibling();
       pFakeRoot->RemoveChildAndNotify(pChild, true);
       GetXFANode()->InsertChildAndNotify(pChild, nullptr);
-      pChild->SetFlagAndNotify(XFA_NodeFlag_Initialized);
+      pChild->SetInitializedFlagAndNotify();
       pChild = pItem;
     }
   }
@@ -366,19 +320,19 @@
   if (pFakeXMLRoot) {
     pFakeRoot->SetXMLMappingNode(std::move(pFakeXMLRoot));
   }
-  pFakeRoot->SetFlag(XFA_NodeFlag_HasRemovedChildren);
+  pFakeRoot->SetFlag(XFA_NodeFlag::kHasRemovedChildren);
 
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Node::saveFilteredXML(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   // TODO(weili): Check whether we need to implement this, pdfium:501.
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Node::saveXML(CFX_V8* runtime,
+CJS_Result CJX_Node::saveXML(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() > 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -416,12 +370,12 @@
     pElement->Save(pMemoryStream);
   }
 
-  return CJS_Result::Success(runtime->NewString(
-      ByteStringView(pMemoryStream->GetBuffer(), pMemoryStream->GetSize())));
+  return CJS_Result::Success(
+      runtime->NewString(ByteStringView(pMemoryStream->GetSpan())));
 }
 
 CJS_Result CJX_Node::setAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -432,12 +386,12 @@
   WideString attribute = runtime->ToWideString(params[1]);
 
   // Pass them to our method, however, in the more usual manner.
-  SetAttribute(attribute.AsStringView(), attributeValue.AsStringView(), true);
+  SetAttributeByString(attribute.AsStringView(), attributeValue);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Node::setElement(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1 && params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -446,66 +400,75 @@
   return CJS_Result::Success();
 }
 
-void CJX_Node::ns(CFXJSE_Value* pValue,
+void CJX_Node::ns(v8::Isolate* pIsolate,
+                  v8::Local<v8::Value>* pValue,
                   bool bSetting,
                   XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString(
-      TryNamespace().value_or(WideString()).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, TryNamespace().value_or(WideString()).ToUTF8().AsStringView());
 }
 
-void CJX_Node::model(CFXJSE_Value* pValue,
+void CJX_Node::model(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pValue,
                      bool bSetting,
                      XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-      GetXFANode()->GetModelNode()));
+  CXFA_Node* pModel = GetXFANode()->GetModelNode();
+  if (!pModel) {
+    *pValue = fxv8::NewNullHelper(pIsolate);
+    return;
+  }
+  *pValue =
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pModel);
 }
 
-void CJX_Node::isContainer(CFXJSE_Value* pValue,
+void CJX_Node::isContainer(v8::Isolate* pIsolate,
+                           v8::Local<v8::Value>* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetBoolean(GetXFANode()->IsContainerNode());
+  *pValue = fxv8::NewBooleanHelper(pIsolate, GetXFANode()->IsContainerNode());
 }
 
-void CJX_Node::isNull(CFXJSE_Value* pValue,
+void CJX_Node::isNull(v8::Isolate* pIsolate,
+                      v8::Local<v8::Value>* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
   if (GetXFANode()->GetElementType() == XFA_Element::Subform) {
-    pValue->SetBoolean(false);
+    *pValue = fxv8::NewBooleanHelper(pIsolate, false);
     return;
   }
-  pValue->SetBoolean(GetContent(false).IsEmpty());
+  *pValue = fxv8::NewBooleanHelper(pIsolate, GetContent(false).IsEmpty());
 }
 
-void CJX_Node::oneOfChild(CFXJSE_Value* pValue,
+void CJX_Node::oneOfChild(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   std::vector<CXFA_Node*> properties =
-      GetXFANode()->GetNodeListWithFilter(XFA_NODEFILTER_OneOfProperty);
+      GetXFANode()->GetNodeListWithFilter(XFA_NodeFilter::kOneOfProperty);
   if (!properties.empty()) {
-    pValue->Assign(
-        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-            properties.front()));
+    *pValue = GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+        properties.front());
   }
 }
 
@@ -515,26 +478,26 @@
   if (!pNotify)
     return XFA_EventError::kNotExist;
 
-  const XFA_ExecEventParaInfo* eventParaInfo =
-      GetEventParaInfoByName(wsEventName);
+  const ExecEventParaInfo* eventParaInfo =
+      GetExecEventParaInfoByName(wsEventName);
   if (!eventParaInfo)
     return XFA_EventError::kNotExist;
 
   switch (eventParaInfo->m_validFlags) {
-    case EventAppliesToo::kNone:
+    case EventAppliesTo::kNone:
       return XFA_EventError::kNotExist;
-    case EventAppliesToo::kAll:
-    case EventAppliesToo::kAllNonRecursive:
+    case EventAppliesTo::kAll:
+    case EventAppliesTo::kAllNonRecursive:
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false,
-          eventParaInfo->m_validFlags == EventAppliesToo::kAll);
-    case EventAppliesToo::kSubform:
+          eventParaInfo->m_validFlags == EventAppliesTo::kAll);
+    case EventAppliesTo::kSubform:
       if (eType != XFA_Element::Subform)
         return XFA_EventError::kNotExist;
 
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
-    case EventAppliesToo::kFieldOrExclusion: {
+    case EventAppliesTo::kFieldOrExclusion: {
       if (eType != XFA_Element::ExclGroup && eType != XFA_Element::Field)
         return XFA_EventError::kNotExist;
 
@@ -548,13 +511,13 @@
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
     }
-    case EventAppliesToo::kField:
+    case EventAppliesTo::kField:
       if (eType != XFA_Element::Field)
         return XFA_EventError::kNotExist;
 
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
-    case EventAppliesToo::kSignature: {
+    case EventAppliesTo::kSignature: {
       if (!GetXFANode()->IsWidgetReady())
         return XFA_EventError::kNotExist;
       if (GetXFANode()->GetUIChildNode()->GetElementType() !=
@@ -564,7 +527,7 @@
       return pNotify->ExecEventByDeepFirst(
           GetXFANode(), eventParaInfo->m_eventType, false, false);
     }
-    case EventAppliesToo::kChoiceList: {
+    case EventAppliesTo::kChoiceList: {
       if (!GetXFANode()->IsWidgetReady())
         return XFA_EventError::kNotExist;
       if (GetXFANode()->GetUIChildNode()->GetElementType() !=
diff --git a/fxjs/xfa/cjx_node.h b/fxjs/xfa/cjx_node.h
index 1cbceb6..bc8eb3f 100644
--- a/fxjs/xfa/cjx_node.h
+++ b/fxjs/xfa/cjx_node.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,7 @@
 
 class CJX_Node : public CJX_Tree {
  public:
-  explicit CJX_Node(CXFA_Node* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Node() override;
 
   // CJX_Object:
@@ -40,9 +40,9 @@
   JSE_PROP(ns);
   JSE_PROP(oneOfChild);
 
-  CXFA_Node* GetXFANode() const;
-
  protected:
+  explicit CJX_Node(CXFA_Node* node);
+
   XFA_EventError execSingleEventByName(WideStringView wsEventName,
                                        XFA_Element eType);
 
diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
index 64006b3..9d6cdf9 100644
--- a/fxjs/xfa/cjx_object.cpp
+++ b/fxjs/xfa/cjx_object.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,20 +8,27 @@
 
 #include <set>
 #include <tuple>
+#include <utility>
 
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "fxjs/cjs_result.h"
+#include "fxjs/fxv8.h"
+#include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/xfa/cfxjse_mapmodule.h"
 #include "fxjs/xfa/cjx_boolean.h"
 #include "fxjs/xfa/cjx_draw.h"
 #include "fxjs/xfa/cjx_field.h"
 #include "fxjs/xfa/cjx_instancemanager.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
+#include "v8/include/v8-forward.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/cxfa_ffwidget.h"
@@ -44,33 +51,19 @@
 
 namespace {
 
-void XFA_DeleteWideString(void* pData) {
-  delete static_cast<WideString*>(pData);
-}
-
-void XFA_CopyWideString(void*& pData) {
-  if (!pData)
-    return;
-  pData = new WideString(*reinterpret_cast<WideString*>(pData));
-}
-
-const XFA_MAPDATABLOCKCALLBACKINFO deleteWideStringCallBack = {
-    XFA_DeleteWideString, XFA_CopyWideString};
-
 enum XFA_KEYTYPE {
   XFA_KEYTYPE_Custom,
   XFA_KEYTYPE_Element,
 };
 
-void* GetMapKey_Custom(WideStringView wsKey) {
-  uint32_t dwKey = FX_HashCode_GetW(wsKey, false);
-  return (void*)(uintptr_t)((dwKey << 1) | XFA_KEYTYPE_Custom);
+uint32_t GetMapKey_Custom(WideStringView wsKey) {
+  uint32_t dwKey = FX_HashCode_GetW(wsKey);
+  return ((dwKey << 1) | XFA_KEYTYPE_Custom);
 }
 
-void* GetMapKey_Element(XFA_Element eType, XFA_Attribute eAttribute) {
-  return (void*)(uintptr_t)((static_cast<uint32_t>(eType) << 16) |
-                            (static_cast<uint32_t>(eAttribute) << 8) |
-                            XFA_KEYTYPE_Element);
+uint32_t GetMapKey_Element(XFA_Element eType, XFA_Attribute eAttribute) {
+  return ((static_cast<uint32_t>(eType) << 16) |
+          (static_cast<uint32_t>(eAttribute) << 8) | XFA_KEYTYPE_Element);
 }
 
 std::tuple<int32_t, int32_t, int32_t> StrToRGB(const WideString& strRGB) {
@@ -106,25 +99,18 @@
 
 }  // namespace
 
-struct XFA_MAPDATABLOCK {
-  uint8_t* GetData() const { return (uint8_t*)this + sizeof(XFA_MAPDATABLOCK); }
-
-  const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo;
-  int32_t iBytes;
-};
-
-struct XFA_MAPMODULEDATA {
-  XFA_MAPMODULEDATA() {}
-  ~XFA_MAPMODULEDATA() {}
-
-  std::map<void*, void*> m_ValueMap;
-  std::map<void*, XFA_MAPDATABLOCK*> m_BufferMap;
-};
-
 CJX_Object::CJX_Object(CXFA_Object* obj) : object_(obj) {}
 
-CJX_Object::~CJX_Object() {
-  ClearMapModuleBuffer();
+CJX_Object::~CJX_Object() = default;
+
+CJX_Object* CJX_Object::AsCJXObject() {
+  return this;
+}
+
+void CJX_Object::Trace(cppgc::Visitor* visitor) const {
+  visitor->Trace(object_);
+  visitor->Trace(layout_item_);
+  visitor->Trace(calc_data_);
 }
 
 bool CJX_Object::DynamicTypeIs(TypeTag eType) const {
@@ -140,19 +126,24 @@
   return object_->GetDocument();
 }
 
-void CJX_Object::className(CFXJSE_Value* pValue,
+CXFA_Node* CJX_Object::GetXFANode() const {
+  return ToNode(GetXFAObject());
+}
+
+void CJX_Object::className(v8::Isolate* pIsolate,
+                           v8::Local<v8::Value>* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString(GetXFAObject()->GetClassName());
+  *pValue = fxv8::NewStringHelper(pIsolate, GetXFAObject()->GetClassName());
 }
 
 int32_t CJX_Object::Subform_and_SubformSet_InstanceIndex() {
   int32_t index = 0;
-  for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode;
+  for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode;
        pNode = pNode->GetPrevSibling()) {
     if ((pNode->GetElementType() != XFA_Element::Subform) &&
         (pNode->GetElementType() != XFA_Element::SubformSet)) {
@@ -164,7 +155,7 @@
 }
 
 bool CJX_Object::HasMethod(const WideString& func) const {
-  return pdfium::ContainsKey(method_specs_, func.ToUTF8());
+  return pdfium::Contains(method_specs_, func.ToUTF8());
 }
 
 CJS_Result CJX_Object::RunMethod(
@@ -178,176 +169,176 @@
                     params);
 }
 
-void CJX_Object::ThrowTooManyOccurancesException(const WideString& obj) const {
-  ThrowException(WideString::FromASCII("The element [") + obj +
-                 WideString::FromASCII(
-                     "] has violated its allowable number of occurrences."));
+void CJX_Object::ThrowTooManyOccurrencesException(v8::Isolate* pIsolate,
+                                                  const WideString& obj) const {
+  ThrowException(
+      pIsolate, WideString::FromASCII("The element [") + obj +
+                    WideString::FromASCII(
+                        "] has violated its allowable number of occurrences."));
 }
 
-void CJX_Object::ThrowInvalidPropertyException() const {
-  ThrowException(WideString::FromASCII("Invalid property set operation."));
+void CJX_Object::ThrowInvalidPropertyException(v8::Isolate* pIsolate) const {
+  ThrowException(pIsolate,
+                 WideString::FromASCII("Invalid property set operation."));
 }
 
-void CJX_Object::ThrowIndexOutOfBoundsException() const {
-  ThrowException(WideString::FromASCII("Index value is out of bounds."));
+void CJX_Object::ThrowIndexOutOfBoundsException(v8::Isolate* pIsolate) const {
+  ThrowException(pIsolate,
+                 WideString::FromASCII("Index value is out of bounds."));
 }
 
 void CJX_Object::ThrowParamCountMismatchException(
+    v8::Isolate* pIsolate,
     const WideString& method) const {
   ThrowException(
+      pIsolate,
       WideString::FromASCII("Incorrect number of parameters calling method '") +
-      method + WideString::FromASCII("'."));
+          method + WideString::FromASCII("'."));
 }
 
-void CJX_Object::ThrowArgumentMismatchException() const {
-  ThrowException(WideString::FromASCII(
-      "Argument mismatch in property or function argument."));
+void CJX_Object::ThrowArgumentMismatchException(v8::Isolate* pIsolate) const {
+  ThrowException(pIsolate,
+                 WideString::FromASCII(
+                     "Argument mismatch in property or function argument."));
 }
 
-void CJX_Object::ThrowException(const WideString& str) const {
-  ASSERT(!str.IsEmpty());
-  FXJSE_ThrowMessage(str.ToUTF8().AsStringView());
+void CJX_Object::ThrowException(v8::Isolate* pIsolate,
+                                const WideString& str) const {
+  DCHECK(!str.IsEmpty());
+  FXJSE_ThrowMessage(pIsolate, str.ToUTF8().AsStringView());
 }
 
-bool CJX_Object::HasAttribute(XFA_Attribute eAttr) {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  return HasMapModuleKey(pKey);
+bool CJX_Object::HasAttribute(XFA_Attribute eAttr) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  return HasMapModuleKey(key);
 }
 
-void CJX_Object::SetAttribute(XFA_Attribute eAttr,
-                              WideStringView wsValue,
-                              bool bNotify) {
-  switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) {
+void CJX_Object::SetAttributeByEnum(XFA_Attribute eAttr,
+                                    const WideString& wsValue,
+                                    bool bNotify) {
+  switch (GetXFANode()->GetAttributeType(eAttr)) {
     case XFA_AttributeType::Enum: {
-      Optional<XFA_AttributeValue> item = XFA_GetAttributeValueByName(wsValue);
+      absl::optional<XFA_AttributeValue> item =
+          XFA_GetAttributeValueByName(wsValue.AsStringView());
       SetEnum(eAttr,
-              item ? *item : *(ToNode(GetXFAObject())->GetDefaultEnum(eAttr)),
+              item.has_value() ? item.value()
+                               : GetXFANode()->GetDefaultEnum(eAttr).value(),
               bNotify);
       break;
     }
     case XFA_AttributeType::CData:
-      SetCData(eAttr, WideString(wsValue), bNotify, false);
+      SetCDataImpl(eAttr, WideString(wsValue), bNotify, false);
       break;
     case XFA_AttributeType::Boolean:
       SetBoolean(eAttr, !wsValue.EqualsASCII("0"), bNotify);
       break;
     case XFA_AttributeType::Integer:
       SetInteger(eAttr,
-                 FXSYS_roundf(FXSYS_wcstof(wsValue.unterminated_c_str(),
-                                           wsValue.GetLength(), nullptr)),
+                 FXSYS_roundf(FXSYS_wcstof(wsValue.c_str(), wsValue.GetLength(),
+                                           nullptr)),
                  bNotify);
       break;
     case XFA_AttributeType::Measure:
-      SetMeasure(eAttr, CXFA_Measurement(wsValue), bNotify);
+      SetMeasure(eAttr, CXFA_Measurement(wsValue.AsStringView()), bNotify);
       break;
     default:
       break;
   }
 }
 
-void CJX_Object::SetMapModuleString(void* pKey, WideStringView wsValue) {
-  SetMapModuleBuffer(pKey, const_cast<wchar_t*>(wsValue.unterminated_c_str()),
-                     wsValue.GetLength() * sizeof(wchar_t), nullptr);
-}
-
-void CJX_Object::SetAttribute(WideStringView wsAttr,
-                              WideStringView wsValue,
-                              bool bNotify) {
-  Optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(wsAttr);
+void CJX_Object::SetAttributeByString(WideStringView wsAttr,
+                                      const WideString& wsValue) {
+  absl::optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(wsAttr);
   if (attr.has_value()) {
-    SetAttribute(attr.value().attribute, wsValue, bNotify);
+    SetAttributeByEnum(attr.value().attribute, wsValue, true);
     return;
   }
-  void* pKey = GetMapKey_Custom(wsAttr);
-  SetMapModuleString(pKey, wsValue);
+  uint32_t key = GetMapKey_Custom(wsAttr);
+  SetMapModuleString(key, wsValue);
 }
 
-WideString CJX_Object::GetAttribute(WideStringView attr) {
+WideString CJX_Object::GetAttributeByString(WideStringView attr) const {
+  absl::optional<WideString> result;
+  absl::optional<XFA_ATTRIBUTEINFO> enum_attr = XFA_GetAttributeByName(attr);
+  if (enum_attr.has_value())
+    result = TryAttribute(enum_attr.value().attribute, true);
+  else
+    result = GetMapModuleStringFollowingChain(GetMapKey_Custom(attr));
+  return result.value_or(WideString());
+}
+
+WideString CJX_Object::GetAttributeByEnum(XFA_Attribute attr) const {
   return TryAttribute(attr, true).value_or(WideString());
 }
 
-WideString CJX_Object::GetAttribute(XFA_Attribute attr) {
-  return TryAttribute(attr, true).value_or(WideString());
-}
-
-Optional<WideString> CJX_Object::TryAttribute(XFA_Attribute eAttr,
-                                              bool bUseDefault) {
-  switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) {
+absl::optional<WideString> CJX_Object::TryAttribute(XFA_Attribute eAttr,
+                                                    bool bUseDefault) const {
+  switch (GetXFANode()->GetAttributeType(eAttr)) {
     case XFA_AttributeType::Enum: {
-      Optional<XFA_AttributeValue> value = TryEnum(eAttr, bUseDefault);
-      if (!value)
-        return {};
-      return WideString::FromASCII(XFA_AttributeValueToName(*value));
+      absl::optional<XFA_AttributeValue> value = TryEnum(eAttr, bUseDefault);
+      if (!value.has_value())
+        return absl::nullopt;
+      return WideString::FromASCII(XFA_AttributeValueToName(value.value()));
     }
     case XFA_AttributeType::CData:
       return TryCData(eAttr, bUseDefault);
 
     case XFA_AttributeType::Boolean: {
-      Optional<bool> value = TryBoolean(eAttr, bUseDefault);
-      if (!value)
-        return {};
-      return WideString(*value ? L"1" : L"0");
+      absl::optional<bool> value = TryBoolean(eAttr, bUseDefault);
+      if (!value.has_value())
+        return absl::nullopt;
+      return WideString(value.value() ? L"1" : L"0");
     }
     case XFA_AttributeType::Integer: {
-      Optional<int32_t> iValue = TryInteger(eAttr, bUseDefault);
-      if (!iValue)
-        return {};
-      return WideString::Format(L"%d", *iValue);
+      absl::optional<int32_t> iValue = TryInteger(eAttr, bUseDefault);
+      if (!iValue.has_value())
+        return absl::nullopt;
+      return WideString::FormatInteger(iValue.value());
     }
     case XFA_AttributeType::Measure: {
-      Optional<CXFA_Measurement> value = TryMeasure(eAttr, bUseDefault);
-      if (!value)
-        return {};
-
+      absl::optional<CXFA_Measurement> value = TryMeasure(eAttr, bUseDefault);
+      if (!value.has_value())
+        return absl::nullopt;
       return value->ToString();
     }
     default:
       break;
   }
-  return {};
-}
-
-Optional<WideString> CJX_Object::TryAttribute(WideStringView wsAttr,
-                                              bool bUseDefault) {
-  Optional<XFA_ATTRIBUTEINFO> attr = XFA_GetAttributeByName(wsAttr);
-  if (attr.has_value())
-    return TryAttribute(attr.value().attribute, bUseDefault);
-  return GetMapModuleString(GetMapKey_Custom(wsAttr));
+  return absl::nullopt;
 }
 
 void CJX_Object::RemoveAttribute(WideStringView wsAttr) {
-  void* pKey = GetMapKey_Custom(wsAttr);
-  if (pKey)
-    RemoveMapModuleKey(pKey);
+  RemoveMapModuleKey(GetMapKey_Custom(wsAttr));
 }
 
-Optional<bool> CJX_Object::TryBoolean(XFA_Attribute eAttr, bool bUseDefault) {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  Optional<void*> value = GetMapModuleValue(pKey);
+absl::optional<bool> CJX_Object::TryBoolean(XFA_Attribute eAttr,
+                                            bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<int32_t> value = GetMapModuleValueFollowingChain(key);
   if (value.has_value())
     return !!value.value();
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultBoolean(eAttr);
+    return absl::nullopt;
+  return GetXFANode()->GetDefaultBoolean(eAttr);
 }
 
 void CJX_Object::SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)bValue, bNotify);
+  CFX_XMLElement* elem = SetValue(eAttr, static_cast<int32_t>(bValue), bNotify);
   if (elem) {
     elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
                        bValue ? L"1" : L"0");
   }
 }
 
-bool CJX_Object::GetBoolean(XFA_Attribute eAttr) {
+bool CJX_Object::GetBoolean(XFA_Attribute eAttr) const {
   return TryBoolean(eAttr, true).value_or(false);
 }
 
 void CJX_Object::SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)iValue, bNotify);
+  CFX_XMLElement* elem = SetValue(eAttr, iValue, bNotify);
   if (elem) {
     elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
-                       WideString::Format(L"%d", iValue));
+                       WideString::FormatInteger(iValue));
   }
 }
 
@@ -355,34 +346,32 @@
   return TryInteger(eAttr, true).value_or(0);
 }
 
-Optional<int32_t> CJX_Object::TryInteger(XFA_Attribute eAttr,
-                                         bool bUseDefault) const {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  Optional<void*> value = GetMapModuleValue(pKey);
+absl::optional<int32_t> CJX_Object::TryInteger(XFA_Attribute eAttr,
+                                               bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<int32_t> value = GetMapModuleValueFollowingChain(key);
   if (value.has_value())
-    return static_cast<int32_t>(reinterpret_cast<uintptr_t>(value.value()));
+    return value.value();
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultInteger(eAttr);
+    return absl::nullopt;
+  return GetXFANode()->GetDefaultInteger(eAttr);
 }
 
-Optional<XFA_AttributeValue> CJX_Object::TryEnum(XFA_Attribute eAttr,
-                                                 bool bUseDefault) const {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  Optional<void*> value = GetMapModuleValue(pKey);
-  if (value.has_value()) {
-    return static_cast<XFA_AttributeValue>(
-        reinterpret_cast<uintptr_t>(value.value()));
-  }
+absl::optional<XFA_AttributeValue> CJX_Object::TryEnum(XFA_Attribute eAttr,
+                                                       bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<int32_t> value = GetMapModuleValueFollowingChain(key);
+  if (value.has_value())
+    return static_cast<XFA_AttributeValue>(value.value());
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultEnum(eAttr);
+    return absl::nullopt;
+  return GetXFANode()->GetDefaultEnum(eAttr);
 }
 
 void CJX_Object::SetEnum(XFA_Attribute eAttr,
                          XFA_AttributeValue eValue,
                          bool bNotify) {
-  CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)eValue, bNotify);
+  CFX_XMLElement* elem = SetValue(eAttr, static_cast<int32_t>(eValue), bNotify);
   if (elem) {
     elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)),
                        WideString::FromASCII(XFA_AttributeValueToName(eValue)));
@@ -394,33 +383,36 @@
 }
 
 void CJX_Object::SetMeasure(XFA_Attribute eAttr,
-                            CXFA_Measurement mValue,
+                            const CXFA_Measurement& mValue,
                             bool bNotify) {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  OnChanging(eAttr, bNotify);
-  SetMapModuleBuffer(pKey, &mValue, sizeof(CXFA_Measurement), nullptr);
-  OnChanged(eAttr, bNotify, false);
+  // Can't short-circuit update here when the value is the same since it
+  // might have come from further up the chain from where we are setting it.
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  if (bNotify)
+    OnChanging(eAttr);
+  SetMapModuleMeasurement(key, mValue);
+  if (bNotify)
+    OnChanged(eAttr, false);
 }
 
-Optional<CXFA_Measurement> CJX_Object::TryMeasure(XFA_Attribute eAttr,
-                                                  bool bUseDefault) const {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  void* pValue;
-  int32_t iBytes;
-  if (GetMapModuleBuffer(pKey, &pValue, &iBytes) &&
-      iBytes == sizeof(CXFA_Measurement)) {
-    return *static_cast<CXFA_Measurement*>(pValue);
-  }
+absl::optional<CXFA_Measurement> CJX_Object::TryMeasure(
+    XFA_Attribute eAttr,
+    bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<CXFA_Measurement> result =
+      GetMapModuleMeasurementFollowingChain(key);
+  if (result.has_value())
+    return result.value();
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultMeasurement(eAttr);
+    return absl::nullopt;
+  return GetXFANode()->GetDefaultMeasurement(eAttr);
 }
 
-Optional<float> CJX_Object::TryMeasureAsFloat(XFA_Attribute attr) const {
-  Optional<CXFA_Measurement> measure = TryMeasure(attr, false);
-  if (measure)
-    return measure->ToUnit(XFA_Unit::Pt);
-  return {};
+absl::optional<float> CJX_Object::TryMeasureAsFloat(XFA_Attribute attr) const {
+  absl::optional<CXFA_Measurement> measure = TryMeasure(attr, false);
+  if (!measure.has_value())
+    return absl::nullopt;
+  return measure->ToUnit(XFA_Unit::Pt);
 }
 
 CXFA_Measurement CJX_Object::GetMeasure(XFA_Attribute eAttr) const {
@@ -428,30 +420,33 @@
 }
 
 float CJX_Object::GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const {
-  auto measure = TryMeasure(eAttr, true).value_or(CXFA_Measurement());
-  return measure.ToUnit(unit);
+  return GetMeasure(eAttr).ToUnit(unit);
 }
 
 WideString CJX_Object::GetCData(XFA_Attribute eAttr) const {
   return TryCData(eAttr, true).value_or(WideString());
 }
 
-void CJX_Object::SetCData(XFA_Attribute eAttr,
-                          const WideString& wsValue,
-                          bool bNotify,
-                          bool bScriptModify) {
-  CXFA_Node* xfaObj = ToNode(GetXFAObject());
-  void* pKey = GetMapKey_Element(xfaObj->GetElementType(), eAttr);
-  OnChanging(eAttr, bNotify);
-  if (eAttr == XFA_Attribute::Value) {
-    WideString* pClone = new WideString(wsValue);
-    SetUserData(pKey, pClone, &deleteWideStringCallBack);
-  } else {
-    SetMapModuleString(pKey, wsValue.AsStringView());
+void CJX_Object::SetCData(XFA_Attribute eAttr, const WideString& wsValue) {
+  return SetCDataImpl(eAttr, wsValue, false, false);
+}
+
+void CJX_Object::SetCDataImpl(XFA_Attribute eAttr,
+                              const WideString& wsValue,
+                              bool bNotify,
+                              bool bScriptModify) {
+  CXFA_Node* xfaObj = GetXFANode();
+  uint32_t key = GetMapKey_Element(xfaObj->GetElementType(), eAttr);
+  absl::optional<WideString> old_value = GetMapModuleString(key);
+  if (!old_value.has_value() || old_value.value() != wsValue) {
+    if (bNotify)
+      OnChanging(eAttr);
+    SetMapModuleString(key, wsValue);
     if (eAttr == XFA_Attribute::Name)
       xfaObj->UpdateNameHash();
+    if (bNotify)
+      OnChanged(eAttr, bScriptModify);
   }
-  OnChanged(eAttr, bNotify, bScriptModify);
 
   if (!xfaObj->IsNeedSavingXMLNode() || eAttr == XFA_Attribute::QualifiedName ||
       eAttr == XFA_Attribute::BindingNode) {
@@ -479,57 +474,55 @@
 }
 
 void CJX_Object::SetAttributeValue(const WideString& wsValue,
-                                   const WideString& wsXMLValue,
-                                   bool bNotify,
-                                   bool bScriptModify) {
-  auto* xfaObj = ToNode(GetXFAObject());
-  void* pKey =
-      GetMapKey_Element(xfaObj->GetElementType(), XFA_Attribute::Value);
-
-  OnChanging(XFA_Attribute::Value, bNotify);
-  WideString* pClone = new WideString(wsValue);
-
-  SetUserData(pKey, pClone, &deleteWideStringCallBack);
-  OnChanged(XFA_Attribute::Value, bNotify, bScriptModify);
-
-  if (!xfaObj->IsNeedSavingXMLNode())
-    return;
-
-  xfaObj->SetToXML(wsXMLValue);
+                                   const WideString& wsXMLValue) {
+  SetAttributeValueImpl(wsValue, wsXMLValue, false, false);
 }
 
-Optional<WideString> CJX_Object::TryCData(XFA_Attribute eAttr,
-                                          bool bUseDefault) const {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  if (eAttr == XFA_Attribute::Value) {
-    void* pData;
-    int32_t iBytes = 0;
-    WideString* pStr = nullptr;
-    if (GetMapModuleBuffer(pKey, &pData, &iBytes) && iBytes == sizeof(void*)) {
-      memcpy(&pData, pData, iBytes);
-      pStr = reinterpret_cast<WideString*>(pData);
-    }
-    if (pStr)
-      return *pStr;
-  } else {
-    Optional<WideString> value = GetMapModuleString(pKey);
-    if (value.has_value())
-      return value;
+void CJX_Object::SetAttributeValueImpl(const WideString& wsValue,
+                                       const WideString& wsXMLValue,
+                                       bool bNotify,
+                                       bool bScriptModify) {
+  auto* xfaObj = GetXFANode();
+  uint32_t key =
+      GetMapKey_Element(xfaObj->GetElementType(), XFA_Attribute::Value);
+  absl::optional<WideString> old_value = GetMapModuleString(key);
+  if (!old_value.has_value() || old_value.value() != wsValue) {
+    if (bNotify)
+      OnChanging(XFA_Attribute::Value);
+    SetMapModuleString(key, wsValue);
+    if (bNotify)
+      OnChanged(XFA_Attribute::Value, bScriptModify);
+    if (xfaObj->IsNeedSavingXMLNode())
+      xfaObj->SetToXML(wsXMLValue);
   }
+}
+
+absl::optional<WideString> CJX_Object::TryCData(XFA_Attribute eAttr,
+                                                bool bUseDefault) const {
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<WideString> value = GetMapModuleStringFollowingChain(key);
+  if (value.has_value())
+    return value;
+
   if (!bUseDefault)
-    return {};
-  return ToNode(GetXFAObject())->GetDefaultCData(eAttr);
+    return absl::nullopt;
+
+  return GetXFANode()->GetDefaultCData(eAttr);
 }
 
 CFX_XMLElement* CJX_Object::SetValue(XFA_Attribute eAttr,
-                                     void* pValue,
+                                     int32_t value,
                                      bool bNotify) {
-  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
-  OnChanging(eAttr, bNotify);
-  SetMapModuleValue(pKey, pValue);
-  OnChanged(eAttr, bNotify, false);
-
-  CXFA_Node* pNode = ToNode(GetXFAObject());
+  uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  absl::optional<int32_t> old_value = GetMapModuleValue(key);
+  if (!old_value.has_value() || old_value.value() != value) {
+    if (bNotify)
+      OnChanging(eAttr);
+    SetMapModuleValue(key, value);
+    if (bNotify)
+      OnChanged(eAttr, false);
+  }
+  CXFA_Node* pNode = GetXFANode();
   return pNode->IsNeedSavingXMLNode() ? ToXMLElement(pNode->GetXMLMappingNode())
                                       : nullptr;
 }
@@ -541,9 +534,9 @@
                             bool bSyncData) {
   CXFA_Node* pNode = nullptr;
   CXFA_Node* pBindNode = nullptr;
-  switch (ToNode(GetXFAObject())->GetObjectType()) {
+  switch (GetXFANode()->GetObjectType()) {
     case XFA_ObjectType::ContainerNode: {
-      if (XFA_FieldIsMultiListBox(ToNode(GetXFAObject()))) {
+      if (XFA_FieldIsMultiListBox(GetXFANode())) {
         CXFA_Value* pValue =
             GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
         if (!pValue)
@@ -551,11 +544,11 @@
 
         CXFA_Node* pChildValue = pValue->GetFirstChild();
         pChildValue->JSObject()->SetCData(XFA_Attribute::ContentType,
-                                          L"text/xml", false, false);
+                                          L"text/xml");
         pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                             bScriptModify, false);
 
-        CXFA_Node* pBind = ToNode(GetXFAObject())->GetBindData();
+        CXFA_Node* pBind = GetXFANode()->GetBindData();
         if (bSyncData && pBind) {
           std::vector<WideString> wsSaveTextArray =
               fxcrt::Split(wsContent, L'\n');
@@ -573,8 +566,8 @@
               while (iAddNodes-- > 0) {
                 CXFA_Node* pValueNodes =
                     pBind->CreateSamePacketNode(XFA_Element::DataValue);
-                pValueNodes->JSObject()->SetCData(XFA_Attribute::Name, L"value",
-                                                  false, false);
+                pValueNodes->JSObject()->SetCData(XFA_Attribute::Name,
+                                                  L"value");
                 pValueNodes->CreateXMLMappingNode();
                 pBind->InsertChildAndNotify(pValueNodes, nullptr);
               }
@@ -585,15 +578,15 @@
             }
             valueNodes = pBind->GetNodeListForType(XFA_Element::DataValue);
           }
-          ASSERT(valueNodes.size() == wsSaveTextArray.size());
+          DCHECK_EQ(valueNodes.size(), wsSaveTextArray.size());
           size_t i = 0;
           for (CXFA_Node* pValueNode : valueNodes) {
-            pValueNode->JSObject()->SetAttributeValue(
-                wsSaveTextArray[i], wsSaveTextArray[i], false, false);
+            pValueNode->JSObject()->SetAttributeValue(wsSaveTextArray[i],
+                                                      wsSaveTextArray[i]);
             i++;
           }
           for (auto* pArrayNode : pBind->GetBindItemsCopy()) {
-            if (pArrayNode != ToNode(GetXFAObject())) {
+            if (pArrayNode != GetXFANode()) {
               pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                                  bScriptModify, false);
             }
@@ -601,8 +594,8 @@
         }
         break;
       }
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExclGroup) {
-        pNode = ToNode(GetXFAObject());
+      if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) {
+        pNode = GetXFANode();
       } else {
         CXFA_Value* pValue =
             GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
@@ -610,16 +603,17 @@
           break;
 
         CXFA_Node* pChildValue = pValue->GetFirstChild();
-        ASSERT(pChildValue);
-        pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify,
-                                            bScriptModify, false);
+        if (pChildValue) {
+          pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify,
+                                              bScriptModify, false);
+        }
       }
-      pBindNode = ToNode(GetXFAObject())->GetBindData();
+      pBindNode = GetXFANode()->GetBindData();
       if (pBindNode && bSyncData) {
         pBindNode->JSObject()->SetContent(wsContent, wsXMLValue, bNotify,
                                           bScriptModify, false);
         for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) {
-          if (pArrayNode != ToNode(GetXFAObject())) {
+          if (pArrayNode != GetXFANode()) {
             pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
                                                true, false);
           }
@@ -630,27 +624,23 @@
     }
     case XFA_ObjectType::ContentNode: {
       WideString wsContentType;
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExData) {
-        Optional<WideString> ret =
+      if (GetXFANode()->GetElementType() == XFA_Element::ExData) {
+        absl::optional<WideString> ret =
             TryAttribute(XFA_Attribute::ContentType, false);
-        if (ret)
-          wsContentType = *ret;
+        if (ret.has_value())
+          wsContentType = ret.value();
         if (wsContentType.EqualsASCII("text/html")) {
           wsContentType.clear();
-          SetAttribute(XFA_Attribute::ContentType, wsContentType.AsStringView(),
-                       false);
+          SetAttributeByEnum(XFA_Attribute::ContentType, wsContentType, false);
         }
       }
 
-      CXFA_Node* pContentRawDataNode = ToNode(GetXFAObject())->GetFirstChild();
+      CXFA_Node* pContentRawDataNode = GetXFANode()->GetFirstChild();
       if (!pContentRawDataNode) {
-        pContentRawDataNode =
-            ToNode(GetXFAObject())
-                ->CreateSamePacketNode(wsContentType.EqualsASCII("text/xml")
-                                           ? XFA_Element::Sharpxml
-                                           : XFA_Element::Sharptext);
-        ToNode(GetXFAObject())
-            ->InsertChildAndNotify(pContentRawDataNode, nullptr);
+        pContentRawDataNode = GetXFANode()->CreateSamePacketNode(
+            wsContentType.EqualsASCII("text/xml") ? XFA_Element::Sharpxml
+                                                  : XFA_Element::Sharptext);
+        GetXFANode()->InsertChildAndNotify(pContentRawDataNode, nullptr);
       }
       pContentRawDataNode->JSObject()->SetContent(
           wsContent, wsXMLValue, bNotify, bScriptModify, bSyncData);
@@ -658,13 +648,12 @@
     }
     case XFA_ObjectType::NodeC:
     case XFA_ObjectType::TextNode:
-      pNode = ToNode(GetXFAObject());
+      pNode = GetXFANode();
       break;
     case XFA_ObjectType::NodeV:
-      pNode = ToNode(GetXFAObject());
-      if (bSyncData &&
-          ToNode(GetXFAObject())->GetPacketType() == XFA_PacketType::Form) {
-        CXFA_Node* pParent = ToNode(GetXFAObject())->GetParent();
+      pNode = GetXFANode();
+      if (bSyncData && GetXFANode()->GetPacketType() == XFA_PacketType::Form) {
+        CXFA_Node* pParent = GetXFANode()->GetParent();
         if (pParent) {
           pParent = pParent->GetParent();
         }
@@ -681,16 +670,16 @@
       }
       break;
     default:
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue) {
-        pNode = ToNode(GetXFAObject());
-        pBindNode = ToNode(GetXFAObject());
+      if (GetXFANode()->GetElementType() == XFA_Element::DataValue) {
+        pNode = GetXFANode();
+        pBindNode = GetXFANode();
       }
       break;
   }
   if (!pNode)
     return;
 
-  SetAttributeValue(wsContent, wsXMLValue, bNotify, bScriptModify);
+  SetAttributeValueImpl(wsContent, wsXMLValue, bNotify, bScriptModify);
   if (pBindNode && bSyncData) {
     for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) {
       pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify,
@@ -699,39 +688,39 @@
   }
 }
 
-WideString CJX_Object::GetContent(bool bScriptModify) {
+WideString CJX_Object::GetContent(bool bScriptModify) const {
   return TryContent(bScriptModify, true).value_or(WideString());
 }
 
-Optional<WideString> CJX_Object::TryContent(bool bScriptModify, bool bProto) {
+absl::optional<WideString> CJX_Object::TryContent(bool bScriptModify,
+                                                  bool bProto) const {
   CXFA_Node* pNode = nullptr;
-  switch (ToNode(GetXFAObject())->GetObjectType()) {
+  switch (GetXFANode()->GetObjectType()) {
     case XFA_ObjectType::ContainerNode:
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExclGroup) {
-        pNode = ToNode(GetXFAObject());
+      if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) {
+        pNode = GetXFANode();
       } else {
         CXFA_Value* pValue =
-            ToNode(GetXFAObject())
-                ->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
+            GetXFANode()->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
         if (!pValue)
-          return {};
+          return absl::nullopt;
 
         CXFA_Node* pChildValue = pValue->GetFirstChild();
-        if (pChildValue && XFA_FieldIsMultiListBox(ToNode(GetXFAObject()))) {
-          pChildValue->JSObject()->SetAttribute(XFA_Attribute::ContentType,
-                                                L"text/xml", false);
+        if (pChildValue && XFA_FieldIsMultiListBox(GetXFANode())) {
+          pChildValue->JSObject()->SetAttributeByEnum(
+              XFA_Attribute::ContentType, L"text/xml", false);
         }
-        if (pChildValue)
-          return pChildValue->JSObject()->TryContent(bScriptModify, bProto);
-        return {};
+        if (!pChildValue)
+          return absl::nullopt;
+        return pChildValue->JSObject()->TryContent(bScriptModify, bProto);
       }
       break;
     case XFA_ObjectType::ContentNode: {
-      CXFA_Node* pContentRawDataNode = ToNode(GetXFAObject())->GetFirstChild();
+      CXFA_Node* pContentRawDataNode = GetXFANode()->GetFirstChild();
       if (!pContentRawDataNode) {
         XFA_Element element = XFA_Element::Sharptext;
-        if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExData) {
-          Optional<WideString> contentType =
+        if (GetXFANode()->GetElementType() == XFA_Element::ExData) {
+          absl::optional<WideString> contentType =
               TryAttribute(XFA_Attribute::ContentType, false);
           if (contentType.has_value()) {
             if (contentType.value().EqualsASCII("text/html"))
@@ -740,349 +729,270 @@
               element = XFA_Element::Sharpxml;
           }
         }
-        pContentRawDataNode =
-            ToNode(GetXFAObject())->CreateSamePacketNode(element);
-        ToNode(GetXFAObject())
-            ->InsertChildAndNotify(pContentRawDataNode, nullptr);
+        pContentRawDataNode = GetXFANode()->CreateSamePacketNode(element);
+        GetXFANode()->InsertChildAndNotify(pContentRawDataNode, nullptr);
       }
       return pContentRawDataNode->JSObject()->TryContent(bScriptModify, true);
     }
     case XFA_ObjectType::NodeC:
     case XFA_ObjectType::NodeV:
     case XFA_ObjectType::TextNode:
-      pNode = ToNode(GetXFAObject());
-      FALLTHROUGH;
+      pNode = GetXFANode();
+      [[fallthrough]];
     default:
-      if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue)
-        pNode = ToNode(GetXFAObject());
+      if (GetXFANode()->GetElementType() == XFA_Element::DataValue)
+        pNode = GetXFANode();
       break;
   }
   if (pNode) {
     if (bScriptModify) {
       CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-      pScriptContext->AddNodesOfRunScript(ToNode(GetXFAObject()));
+      pScriptContext->AddNodesOfRunScript(GetXFANode());
     }
     return TryCData(XFA_Attribute::Value, false);
   }
-  return {};
+  return absl::nullopt;
 }
 
-Optional<WideString> CJX_Object::TryNamespace() {
-  if (ToNode(GetXFAObject())->IsModelNode() ||
-      ToNode(GetXFAObject())->GetElementType() == XFA_Element::Packet) {
-    CFX_XMLNode* pXMLNode = ToNode(GetXFAObject())->GetXMLMappingNode();
+absl::optional<WideString> CJX_Object::TryNamespace() const {
+  if (GetXFANode()->IsModelNode() ||
+      GetXFANode()->GetElementType() == XFA_Element::Packet) {
+    CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
     CFX_XMLElement* element = ToXMLElement(pXMLNode);
     if (!element)
-      return {};
+      return absl::nullopt;
 
     return element->GetNamespaceURI();
   }
 
-  if (ToNode(GetXFAObject())->GetPacketType() != XFA_PacketType::Datasets)
-    return ToNode(GetXFAObject())->GetModelNode()->JSObject()->TryNamespace();
+  if (GetXFANode()->GetPacketType() != XFA_PacketType::Datasets)
+    return GetXFANode()->GetModelNode()->JSObject()->TryNamespace();
 
-  CFX_XMLNode* pXMLNode = ToNode(GetXFAObject())->GetXMLMappingNode();
+  CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
   CFX_XMLElement* element = ToXMLElement(pXMLNode);
   if (!element)
-    return {};
+    return absl::nullopt;
 
-  if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue &&
+  if (GetXFANode()->GetElementType() == XFA_Element::DataValue &&
       GetEnum(XFA_Attribute::Contains) == XFA_AttributeValue::MetaData) {
     WideString wsNamespace;
     if (!XFA_FDEExtension_ResolveNamespaceQualifier(
             element, GetCData(XFA_Attribute::QualifiedName), &wsNamespace)) {
-      return {};
+      return absl::nullopt;
     }
     return wsNamespace;
   }
   return element->GetNamespaceURI();
 }
 
-std::pair<CXFA_Node*, int32_t> CJX_Object::GetPropertyInternal(
-    int32_t index,
-    XFA_Element eProperty) const {
-  return ToNode(GetXFAObject())->GetProperty(index, eProperty);
+CXFA_Node* CJX_Object::GetPropertyInternal(int32_t index,
+                                           XFA_Element eProperty) const {
+  return GetXFANode()->GetProperty(index, eProperty).first;
 }
 
 CXFA_Node* CJX_Object::GetOrCreatePropertyInternal(int32_t index,
                                                    XFA_Element eProperty) {
-  return ToNode(GetXFAObject())->GetOrCreateProperty(index, eProperty);
+  return GetXFANode()->GetOrCreateProperty(index, eProperty);
 }
 
-void CJX_Object::SetUserData(
-    void* pKey,
-    void* pData,
-    const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
-  ASSERT(pCallbackInfo);
-  SetMapModuleBuffer(pKey, &pData, sizeof(void*), pCallbackInfo);
+CFXJSE_MapModule* CJX_Object::CreateMapModule() {
+  if (!map_module_)
+    map_module_ = std::make_unique<CFXJSE_MapModule>();
+  return map_module_.get();
 }
 
-XFA_MAPMODULEDATA* CJX_Object::CreateMapModuleData() {
-  if (!map_module_data_)
-    map_module_data_ = pdfium::MakeUnique<XFA_MAPMODULEDATA>();
-  return map_module_data_.get();
+CFXJSE_MapModule* CJX_Object::GetMapModule() const {
+  return map_module_.get();
 }
 
-XFA_MAPMODULEDATA* CJX_Object::GetMapModuleData() const {
-  return map_module_data_.get();
+void CJX_Object::SetMapModuleValue(uint32_t key, int32_t value) {
+  CreateMapModule()->SetValue(key, value);
 }
 
-void CJX_Object::SetMapModuleValue(void* pKey, void* pValue) {
-  CreateMapModuleData()->m_ValueMap[pKey] = pValue;
+void CJX_Object::SetMapModuleString(uint32_t key, const WideString& wsValue) {
+  CreateMapModule()->SetString(key, wsValue);
 }
 
-Optional<void*> CJX_Object::GetMapModuleValue(void* pKey) const {
+void CJX_Object::SetMapModuleMeasurement(uint32_t key,
+                                         const CXFA_Measurement& value) {
+  CreateMapModule()->SetMeasurement(key, value);
+}
+
+absl::optional<int32_t> CJX_Object::GetMapModuleValue(uint32_t key) const {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  if (!pModule)
+    return absl::nullopt;
+  return pModule->GetValue(key);
+}
+
+absl::optional<WideString> CJX_Object::GetMapModuleString(uint32_t key) const {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  if (!pModule)
+    return absl::nullopt;
+  return pModule->GetString(key);
+}
+
+absl::optional<CXFA_Measurement> CJX_Object::GetMapModuleMeasurement(
+    uint32_t key) const {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  if (!pModule)
+    return absl::nullopt;
+  return pModule->GetMeasurement(key);
+}
+
+absl::optional<int32_t> CJX_Object::GetMapModuleValueFollowingChain(
+    uint32_t key) const {
   std::set<const CXFA_Node*> visited;
-  for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
+  for (const CXFA_Node* pNode = GetXFANode(); pNode;
        pNode = pNode->GetTemplateNodeIfExists()) {
     if (!visited.insert(pNode).second)
       break;
 
-    XFA_MAPMODULEDATA* pModule = pNode->JSObject()->GetMapModuleData();
-    if (pModule) {
-      auto it = pModule->m_ValueMap.find(pKey);
-      if (it != pModule->m_ValueMap.end())
-        return it->second;
-    }
+    absl::optional<int32_t> result = pNode->JSObject()->GetMapModuleValue(key);
+    if (result.has_value())
+      return result;
+
     if (pNode->GetPacketType() == XFA_PacketType::Datasets)
       break;
   }
-  return {};
+  return absl::nullopt;
 }
 
-Optional<WideString> CJX_Object::GetMapModuleString(void* pKey) const {
-  void* pRawValue;
-  int32_t iBytes;
-  if (!GetMapModuleBuffer(pKey, &pRawValue, &iBytes))
-    return {};
-
-  // Defensive measure: no out-of-bounds pointers even if zero length.
-  int32_t iChars = iBytes / sizeof(wchar_t);
-  return WideString(iChars ? static_cast<const wchar_t*>(pRawValue) : nullptr,
-                    iChars);
-}
-
-void CJX_Object::SetMapModuleBuffer(
-    void* pKey,
-    void* pValue,
-    int32_t iBytes,
-    const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
-  XFA_MAPDATABLOCK*& pBuffer = CreateMapModuleData()->m_BufferMap[pKey];
-  if (!pBuffer) {
-    pBuffer = reinterpret_cast<XFA_MAPDATABLOCK*>(
-        FX_Alloc(uint8_t, sizeof(XFA_MAPDATABLOCK) + iBytes));
-  } else if (pBuffer->iBytes != iBytes) {
-    if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree)
-      pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData());
-
-    pBuffer = reinterpret_cast<XFA_MAPDATABLOCK*>(
-        FX_Realloc(uint8_t, pBuffer, sizeof(XFA_MAPDATABLOCK) + iBytes));
-  } else if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree) {
-    pBuffer->pCallbackInfo->pFree(
-        *reinterpret_cast<void**>(pBuffer->GetData()));
-  }
-  if (!pBuffer)
-    return;
-
-  pBuffer->pCallbackInfo = pCallbackInfo;
-  pBuffer->iBytes = iBytes;
-  memcpy(pBuffer->GetData(), pValue, iBytes);
-}
-
-bool CJX_Object::GetMapModuleBuffer(void* pKey,
-                                    void** pValue,
-                                    int32_t* pBytes) const {
+absl::optional<WideString> CJX_Object::GetMapModuleStringFollowingChain(
+    uint32_t key) const {
   std::set<const CXFA_Node*> visited;
-  XFA_MAPDATABLOCK* pBuffer = nullptr;
-  for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
+  for (const CXFA_Node* pNode = GetXFANode(); pNode;
        pNode = pNode->GetTemplateNodeIfExists()) {
     if (!visited.insert(pNode).second)
       break;
 
-    XFA_MAPMODULEDATA* pModule = pNode->JSObject()->GetMapModuleData();
-    if (pModule) {
-      auto it = pModule->m_BufferMap.find(pKey);
-      if (it != pModule->m_BufferMap.end()) {
-        pBuffer = it->second;
-        break;
-      }
-    }
+    absl::optional<WideString> result =
+        pNode->JSObject()->GetMapModuleString(key);
+    if (result.has_value())
+      return result;
+
     if (pNode->GetPacketType() == XFA_PacketType::Datasets)
       break;
   }
-  if (!pBuffer)
-    return false;
-
-  *pValue = pBuffer->GetData();
-  *pBytes = pBuffer->iBytes;
-  return true;
+  return absl::nullopt;
 }
 
-bool CJX_Object::HasMapModuleKey(void* pKey) {
-  XFA_MAPMODULEDATA* pModule = GetMapModuleData();
-  return pModule && (pdfium::ContainsKey(pModule->m_ValueMap, pKey) ||
-                     pdfium::ContainsKey(pModule->m_BufferMap, pKey));
-}
+absl::optional<CXFA_Measurement>
+CJX_Object::GetMapModuleMeasurementFollowingChain(uint32_t key) const {
+  std::set<const CXFA_Node*> visited;
+  for (const CXFA_Node* pNode = GetXFANode(); pNode;
+       pNode = pNode->GetTemplateNodeIfExists()) {
+    if (!visited.insert(pNode).second)
+      break;
 
-void CJX_Object::ClearMapModuleBuffer() {
-  XFA_MAPMODULEDATA* pModule = GetMapModuleData();
-  if (!pModule)
-    return;
+    absl::optional<CXFA_Measurement> result =
+        pNode->JSObject()->GetMapModuleMeasurement(key);
+    if (result.has_value())
+      return result;
 
-  for (auto& pair : pModule->m_BufferMap) {
-    XFA_MAPDATABLOCK* pBuffer = pair.second;
-    if (pBuffer) {
-      if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree)
-        pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData());
-
-      FX_Free(pBuffer);
-    }
+    if (pNode->GetPacketType() == XFA_PacketType::Datasets)
+      break;
   }
-  pModule->m_BufferMap.clear();
-  pModule->m_ValueMap.clear();
+  return absl::nullopt;
 }
 
-void CJX_Object::RemoveMapModuleKey(void* pKey) {
-  ASSERT(pKey);
-
-  XFA_MAPMODULEDATA* pModule = GetMapModuleData();
-  if (!pModule)
-    return;
-
-  auto it = pModule->m_BufferMap.find(pKey);
-  if (it != pModule->m_BufferMap.end()) {
-    XFA_MAPDATABLOCK* pBuffer = it->second;
-    if (pBuffer) {
-      if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree)
-        pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData());
-
-      FX_Free(pBuffer);
-    }
-    pModule->m_BufferMap.erase(it);
-  }
-  pModule->m_ValueMap.erase(pKey);
-  return;
+bool CJX_Object::HasMapModuleKey(uint32_t key) const {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  return pModule && pModule->HasKey(key);
 }
 
-void CJX_Object::MergeAllData(CXFA_Object* pDstModule) {
-  XFA_MAPMODULEDATA* pDstModuleData =
-      ToNode(pDstModule)->JSObject()->CreateMapModuleData();
-  XFA_MAPMODULEDATA* pSrcModuleData = GetMapModuleData();
-  if (!pSrcModuleData)
-    return;
-
-  for (const auto& pair : pSrcModuleData->m_ValueMap)
-    pDstModuleData->m_ValueMap[pair.first] = pair.second;
-
-  for (const auto& pair : pSrcModuleData->m_BufferMap) {
-    XFA_MAPDATABLOCK* pSrcBuffer = pair.second;
-    XFA_MAPDATABLOCK*& pDstBuffer = pDstModuleData->m_BufferMap[pair.first];
-    if (pSrcBuffer->pCallbackInfo && pSrcBuffer->pCallbackInfo->pFree &&
-        !pSrcBuffer->pCallbackInfo->pCopy) {
-      if (pDstBuffer) {
-        pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData());
-        pDstModuleData->m_BufferMap.erase(pair.first);
-      }
-      continue;
-    }
-    if (!pDstBuffer) {
-      pDstBuffer = (XFA_MAPDATABLOCK*)FX_Alloc(
-          uint8_t, sizeof(XFA_MAPDATABLOCK) + pSrcBuffer->iBytes);
-    } else if (pDstBuffer->iBytes != pSrcBuffer->iBytes) {
-      if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pFree) {
-        pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData());
-      }
-      pDstBuffer = (XFA_MAPDATABLOCK*)FX_Realloc(
-          uint8_t, pDstBuffer, sizeof(XFA_MAPDATABLOCK) + pSrcBuffer->iBytes);
-    } else if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pFree) {
-      pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData());
-    }
-    if (!pDstBuffer)
-      continue;
-
-    pDstBuffer->pCallbackInfo = pSrcBuffer->pCallbackInfo;
-    pDstBuffer->iBytes = pSrcBuffer->iBytes;
-    memcpy(pDstBuffer->GetData(), pSrcBuffer->GetData(), pSrcBuffer->iBytes);
-    if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pCopy) {
-      pDstBuffer->pCallbackInfo->pCopy(*(void**)pDstBuffer->GetData());
-    }
-  }
+void CJX_Object::RemoveMapModuleKey(uint32_t key) {
+  CFXJSE_MapModule* pModule = GetMapModule();
+  if (pModule)
+    pModule->RemoveKey(key);
 }
 
-void CJX_Object::MoveBufferMapData(CXFA_Object* pDstModule) {
-  if (!pDstModule)
+void CJX_Object::MergeAllData(CXFA_Object* pDstObj) {
+  CFXJSE_MapModule* pDstModule = ToNode(pDstObj)->JSObject()->CreateMapModule();
+  CFXJSE_MapModule* pSrcModule = GetMapModule();
+  if (!pSrcModule)
     return;
 
-  bool bNeedMove = true;
-  if (pDstModule->GetElementType() != GetXFAObject()->GetElementType())
-    bNeedMove = false;
+  pDstModule->MergeDataFrom(pSrcModule);
+}
 
-  if (bNeedMove)
-    ToNode(pDstModule)->JSObject()->SetCalcData(ReleaseCalcData());
-  if (!pDstModule->IsNodeV())
+void CJX_Object::MoveBufferMapData(CXFA_Object* pDstObj) {
+  if (!pDstObj)
     return;
 
-  WideString wsValue = ToNode(pDstModule)->JSObject()->GetContent(false);
+  if (pDstObj->GetElementType() == GetXFAObject()->GetElementType())
+    ToNode(pDstObj)->JSObject()->TakeCalcDataFrom(this);
+
+  if (!pDstObj->IsNodeV())
+    return;
+
+  WideString wsValue = ToNode(pDstObj)->JSObject()->GetContent(false);
   WideString wsFormatValue(wsValue);
-  CXFA_Node* pNode = ToNode(pDstModule)->GetContainerNode();
+  CXFA_Node* pNode = ToNode(pDstObj)->GetContainerNode();
   if (pNode)
     wsFormatValue = pNode->GetFormatDataValue(wsValue);
 
-  ToNode(pDstModule)
-      ->JSObject()
-      ->SetContent(wsValue, wsFormatValue, true, true, true);
+  ToNode(pDstObj)->JSObject()->SetContent(wsValue, wsFormatValue, true, true,
+                                          true);
 }
 
-void CJX_Object::MoveBufferMapData(CXFA_Object* pSrcModule,
-                                   CXFA_Object* pDstModule) {
-  if (!pSrcModule || !pDstModule)
+void CJX_Object::MoveBufferMapData(CXFA_Object* pSrcObj, CXFA_Object* pDstObj) {
+  if (!pSrcObj || !pDstObj)
     return;
 
-  CXFA_Node* pSrcChild = ToNode(pSrcModule)->GetFirstChild();
-  CXFA_Node* pDstChild = ToNode(pDstModule)->GetFirstChild();
+  CXFA_Node* pSrcChild = ToNode(pSrcObj)->GetFirstChild();
+  CXFA_Node* pDstChild = ToNode(pDstObj)->GetFirstChild();
   while (pSrcChild && pDstChild) {
     MoveBufferMapData(pSrcChild, pDstChild);
-
     pSrcChild = pSrcChild->GetNextSibling();
     pDstChild = pDstChild->GetNextSibling();
   }
-  ToNode(pSrcModule)->JSObject()->MoveBufferMapData(pDstModule);
+  ToNode(pSrcObj)->JSObject()->MoveBufferMapData(pDstObj);
 }
 
-void CJX_Object::OnChanging(XFA_Attribute eAttr, bool bNotify) {
-  if (!bNotify || !ToNode(GetXFAObject())->IsInitialized())
+void CJX_Object::OnChanging(XFA_Attribute eAttr) {
+  if (!GetXFANode()->IsInitialized())
     return;
 
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
-  if (pNotify)
-    pNotify->OnValueChanging(ToNode(GetXFAObject()), eAttr);
+  if (!pNotify)
+    return;
+
+  pNotify->OnValueChanging(GetXFANode(), eAttr);
 }
 
-void CJX_Object::OnChanged(XFA_Attribute eAttr,
-                           bool bNotify,
-                           bool bScriptModify) {
-  if (bNotify && ToNode(GetXFAObject())->IsInitialized())
-    ToNode(GetXFAObject())->SendAttributeChangeMessage(eAttr, bScriptModify);
+void CJX_Object::OnChanged(XFA_Attribute eAttr, bool bScriptModify) {
+  if (!GetXFANode()->IsInitialized())
+    return;
+
+  GetXFANode()->SendAttributeChangeMessage(eAttr, bScriptModify);
 }
 
-void CJX_Object::SetCalcData(std::unique_ptr<CXFA_CalcData> data) {
-  calc_data_ = std::move(data);
+CJX_Object::CalcData* CJX_Object::GetOrCreateCalcData(cppgc::Heap* heap) {
+  if (!calc_data_) {
+    calc_data_ =
+        cppgc::MakeGarbageCollected<CalcData>(heap->GetAllocationHandle());
+  }
+  return calc_data_;
 }
 
-std::unique_ptr<CXFA_CalcData> CJX_Object::ReleaseCalcData() {
-  return std::move(calc_data_);
+void CJX_Object::TakeCalcDataFrom(CJX_Object* that) {
+  calc_data_ = that->calc_data_;
+  that->calc_data_ = nullptr;
 }
 
-void CJX_Object::ScriptAttributeString(CFXJSE_Value* pValue,
+void CJX_Object::ScriptAttributeString(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value>* pValue,
                                        bool bSetting,
                                        XFA_Attribute eAttribute) {
   if (!bSetting) {
-    pValue->SetString(GetAttribute(eAttribute).ToUTF8().AsStringView());
+    *pValue = fxv8::NewStringHelper(
+        pIsolate, GetAttributeByEnum(eAttribute).ToUTF8().AsStringView());
     return;
   }
 
-  WideString wsValue = pValue->ToWideString();
-  SetAttribute(eAttribute, wsValue.AsStringView(), true);
+  WideString wsValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
+  SetAttributeByEnum(eAttribute, wsValue, true);
   if (eAttribute != XFA_Attribute::Use ||
       GetXFAObject()->GetElementType() != XFA_Element::Desc) {
     return;
@@ -1100,33 +1010,34 @@
   WideString wsSOM;
   if (!wsValue.IsEmpty()) {
     if (wsValue[0] == '#')
-      wsID = wsValue.Substr(1, wsValue.GetLength() - 1);
+      wsID = wsValue.Substr(1);
     else
       wsSOM = std::move(wsValue);
   }
 
   CXFA_Node* pProtoNode = nullptr;
   if (!wsSOM.IsEmpty()) {
-    XFA_RESOLVENODE_RS resolveNodeRS;
-    bool bRet = GetDocument()->GetScriptContext()->ResolveObjects(
-        pProtoRoot, wsSOM.AsStringView(), &resolveNodeRS,
-        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-            XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
-            XFA_RESOLVENODE_Siblings,
-        nullptr);
-    if (bRet && resolveNodeRS.objects.front()->IsNode())
-      pProtoNode = resolveNodeRS.objects.front()->AsNode();
-
+    absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+        GetDocument()->GetScriptContext()->ResolveObjects(
+            pProtoRoot, wsSOM.AsStringView(),
+            Mask<XFA_ResolveFlag>{
+                XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
+                XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
+                XFA_ResolveFlag::kSiblings});
+    if (maybeResult.has_value() &&
+        maybeResult.value().objects.front()->IsNode()) {
+      pProtoNode = maybeResult.value().objects.front()->AsNode();
+    }
   } else if (!wsID.IsEmpty()) {
     pProtoNode = GetDocument()->GetNodeByID(pProtoRoot, wsID.AsStringView());
   }
-  if (!pProtoNode)
+  if (!pProtoNode || pProtoNode->GetPacketType() != XFA_PacketType::Template)
     return;
 
-  CXFA_Node* pHeadChild = ToNode(GetXFAObject())->GetFirstChild();
+  CXFA_Node* pHeadChild = GetXFANode()->GetFirstChild();
   while (pHeadChild) {
     CXFA_Node* pSibling = pHeadChild->GetNextSibling();
-    ToNode(GetXFAObject())->RemoveChildAndNotify(pHeadChild, true);
+    GetXFANode()->RemoveChildAndNotify(pHeadChild, true);
     pHeadChild = pSibling;
   }
 
@@ -1135,32 +1046,37 @@
   while (pHeadChild) {
     CXFA_Node* pSibling = pHeadChild->GetNextSibling();
     pProtoForm->RemoveChildAndNotify(pHeadChild, true);
-    ToNode(GetXFAObject())->InsertChildAndNotify(pHeadChild, nullptr);
+    GetXFANode()->InsertChildAndNotify(pHeadChild, nullptr);
     pHeadChild = pSibling;
   }
 }
 
-void CJX_Object::ScriptAttributeBool(CFXJSE_Value* pValue,
+void CJX_Object::ScriptAttributeBool(v8::Isolate* pIsolate,
+                                     v8::Local<v8::Value>* pValue,
                                      bool bSetting,
                                      XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetBoolean(eAttribute, pValue->ToBoolean(), true);
+    SetBoolean(eAttribute, fxv8::ReentrantToBooleanHelper(pIsolate, *pValue),
+               true);
     return;
   }
-  pValue->SetString(GetBoolean(eAttribute) ? "1" : "0");
+  *pValue = fxv8::NewStringHelper(pIsolate, GetBoolean(eAttribute) ? "1" : "0");
 }
 
-void CJX_Object::ScriptAttributeInteger(CFXJSE_Value* pValue,
+void CJX_Object::ScriptAttributeInteger(v8::Isolate* pIsolate,
+                                        v8::Local<v8::Value>* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetInteger(eAttribute, pValue->ToInteger(), true);
+    SetInteger(eAttribute, fxv8::ReentrantToInt32Helper(pIsolate, *pValue),
+               true);
     return;
   }
-  pValue->SetInteger(GetInteger(eAttribute));
+  *pValue = fxv8::NewNumberHelper(pIsolate, GetInteger(eAttribute));
 }
 
-void CJX_Object::ScriptSomFontColor(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomFontColor(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
   CXFA_Font* font = ToNode(object_.Get())->GetOrCreateFontIfPossible();
@@ -1171,7 +1087,8 @@
     int32_t r;
     int32_t g;
     int32_t b;
-    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    std::tie(r, g, b) =
+        StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     FX_ARGB color = ArgbEncode(0xff, r, g, b);
     font->SetColor(color);
     return;
@@ -1182,10 +1099,12 @@
   int32_t g;
   int32_t b;
   std::tie(a, r, g, b) = ArgbDecode(font->GetColor());
-  pValue->SetString(ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
 }
 
-void CJX_Object::ScriptSomFillColor(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomFillColor(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
@@ -1197,23 +1116,25 @@
     int32_t r;
     int32_t g;
     int32_t b;
-    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    std::tie(r, g, b) =
+        StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     FX_ARGB color = ArgbEncode(0xff, r, g, b);
     borderfill->SetColor(color);
     return;
   }
 
-  FX_ARGB color = borderfill->GetColor(false);
+  FX_ARGB color = borderfill->GetFillColor();
   int32_t a;
   int32_t r;
   int32_t g;
   int32_t b;
   std::tie(a, r, g, b) = ArgbDecode(color);
-  pValue->SetString(
-      WideString::Format(L"%d,%d,%d", r, g, b).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
 }
 
-void CJX_Object::ScriptSomBorderColor(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomBorderColor(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Value>* pValue,
                                       bool bSetting,
                                       XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
@@ -1222,7 +1143,8 @@
     int32_t r = 0;
     int32_t g = 0;
     int32_t b = 0;
-    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    std::tie(r, g, b) =
+        StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     FX_ARGB rgb = ArgbEncode(100, r, g, b);
     for (int32_t i = 0; i < iSize; ++i) {
       CXFA_Edge* edge = border->GetEdgeIfExists(i);
@@ -1240,11 +1162,12 @@
   int32_t g;
   int32_t b;
   std::tie(a, r, g, b) = ArgbDecode(color);
-  pValue->SetString(
-      WideString::Format(L"%d,%d,%d", r, g, b).ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
 }
 
-void CJX_Object::ScriptSomBorderWidth(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomBorderWidth(v8::Isolate* pIsolate,
+                                      v8::Local<v8::Value>* pValue,
                                       bool bSetting,
                                       XFA_Attribute eAttribute) {
   CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible();
@@ -1252,24 +1175,26 @@
     CXFA_Edge* edge = border->GetEdgeIfExists(0);
     CXFA_Measurement thickness =
         edge ? edge->GetMSThickness() : CXFA_Measurement(0.5, XFA_Unit::Pt);
-    pValue->SetString(thickness.ToString().ToUTF8().AsStringView());
+    *pValue = fxv8::NewStringHelper(
+        pIsolate, thickness.ToString().ToUTF8().AsStringView());
     return;
   }
 
   if (pValue->IsEmpty())
     return;
 
-  WideString wsThickness = pValue->ToWideString();
-  for (int32_t i = 0; i < border->CountEdges(); ++i) {
+  WideString wsThickness = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
+  for (size_t i = 0; i < border->CountEdges(); ++i) {
     CXFA_Edge* edge = border->GetEdgeIfExists(i);
     if (edge)
       edge->SetMSThickness(CXFA_Measurement(wsThickness.AsStringView()));
   }
 }
 
-void CJX_Object::ScriptSomMessage(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomMessage(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
-                                  XFA_SOM_MESSAGETYPE iMessageType) {
+                                  SOMMessageType iMessageType) {
   bool bNew = false;
   CXFA_Validate* validate = ToNode(object_.Get())->GetValidateIfExists();
   if (!validate) {
@@ -1280,16 +1205,17 @@
   if (bSetting) {
     if (validate) {
       switch (iMessageType) {
-        case XFA_SOM_ValidationMessage:
-          validate->SetScriptMessageText(pValue->ToWideString());
+        case SOMMessageType::kValidationMessage:
+          validate->SetScriptMessageText(
+              fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
           break;
-        case XFA_SOM_FormatMessage:
-          validate->SetFormatMessageText(pValue->ToWideString());
+        case SOMMessageType::kFormatMessage:
+          validate->SetFormatMessageText(
+              fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
           break;
-        case XFA_SOM_MandatoryMessage:
-          validate->SetNullMessageText(pValue->ToWideString());
-          break;
-        default:
+        case SOMMessageType::kMandatoryMessage:
+          validate->SetNullMessageText(
+              fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
           break;
       }
     }
@@ -1299,98 +1225,100 @@
       if (!pNotify)
         return;
 
-      pNotify->AddCalcValidate(ToNode(GetXFAObject()));
+      pNotify->AddCalcValidate(GetXFANode());
     }
     return;
   }
 
   if (!validate) {
     // TODO(dsinclair): Better error message?
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   WideString wsMessage;
   switch (iMessageType) {
-    case XFA_SOM_ValidationMessage:
+    case SOMMessageType::kValidationMessage:
       wsMessage = validate->GetScriptMessageText();
       break;
-    case XFA_SOM_FormatMessage:
+    case SOMMessageType::kFormatMessage:
       wsMessage = validate->GetFormatMessageText();
       break;
-    case XFA_SOM_MandatoryMessage:
+    case SOMMessageType::kMandatoryMessage:
       wsMessage = validate->GetNullMessageText();
       break;
-    default:
-      break;
   }
-  pValue->SetString(wsMessage.ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, wsMessage.ToUTF8().AsStringView());
 }
 
-void CJX_Object::ScriptSomValidationMessage(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomValidationMessage(v8::Isolate* pIsolate,
+                                            v8::Local<v8::Value>* pValue,
                                             bool bSetting,
                                             XFA_Attribute eAttribute) {
-  ScriptSomMessage(pValue, bSetting, XFA_SOM_ValidationMessage);
+  ScriptSomMessage(pIsolate, pValue, bSetting,
+                   SOMMessageType::kValidationMessage);
 }
 
-void CJX_Object::ScriptSomMandatoryMessage(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomMandatoryMessage(v8::Isolate* pIsolate,
+                                           v8::Local<v8::Value>* pValue,
                                            bool bSetting,
                                            XFA_Attribute eAttribute) {
-  ScriptSomMessage(pValue, bSetting, XFA_SOM_MandatoryMessage);
+  ScriptSomMessage(pIsolate, pValue, bSetting,
+                   SOMMessageType::kMandatoryMessage);
 }
 
-void CJX_Object::ScriptSomDefaultValue(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomDefaultValue(v8::Isolate* pIsolate,
+                                       v8::Local<v8::Value>* pValue,
                                        bool bSetting,
                                        XFA_Attribute /* unused */) {
-  XFA_Element eType = ToNode(GetXFAObject())->GetElementType();
+  XFA_Element eType = GetXFANode()->GetElementType();
 
   // TODO(dsinclair): This should look through the properties on the node to see
   // if defaultValue is defined and, if so, call that one. Just have to make
   // sure that those defaultValue calls don't call back to this one ....
   if (eType == XFA_Element::Field) {
-    static_cast<CJX_Field*>(this)->defaultValue(pValue, bSetting,
+    static_cast<CJX_Field*>(this)->defaultValue(pIsolate, pValue, bSetting,
                                                 XFA_Attribute::Unknown);
     return;
   }
   if (eType == XFA_Element::Draw) {
-    static_cast<CJX_Draw*>(this)->defaultValue(pValue, bSetting,
+    static_cast<CJX_Draw*>(this)->defaultValue(pIsolate, pValue, bSetting,
                                                XFA_Attribute::Unknown);
     return;
   }
   if (eType == XFA_Element::Boolean) {
-    static_cast<CJX_Boolean*>(this)->defaultValue(pValue, bSetting,
+    static_cast<CJX_Boolean*>(this)->defaultValue(pIsolate, pValue, bSetting,
                                                   XFA_Attribute::Unknown);
     return;
   }
 
   if (bSetting) {
     WideString wsNewValue;
-    if (pValue &&
-        !(pValue->IsEmpty() || pValue->IsNull() || pValue->IsUndefined())) {
-      wsNewValue = pValue->ToWideString();
+    if (pValue && !(pValue->IsEmpty() || fxv8::IsNull(*pValue) ||
+                    fxv8::IsUndefined(*pValue))) {
+      wsNewValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
     }
 
-    WideString wsFormatValue(wsNewValue);
+    WideString wsFormatValue = wsNewValue;
     CXFA_Node* pContainerNode = nullptr;
-    if (ToNode(GetXFAObject())->GetPacketType() == XFA_PacketType::Datasets) {
+    if (GetXFANode()->GetPacketType() == XFA_PacketType::Datasets) {
       WideString wsPicture;
-      for (auto* pFormNode : ToNode(GetXFAObject())->GetBindItemsCopy()) {
+      for (auto* pFormNode : GetXFANode()->GetBindItemsCopy()) {
         if (!pFormNode || pFormNode->HasRemovedChildren())
           continue;
 
         pContainerNode = pFormNode->GetContainerNode();
         if (pContainerNode) {
           wsPicture =
-              pContainerNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
+              pContainerNode->GetPictureContent(XFA_ValuePicture::kDataBind);
         }
         if (!wsPicture.IsEmpty())
           break;
 
         pContainerNode = nullptr;
       }
-    } else if (ToNode(GetXFAObject())->GetPacketType() ==
-               XFA_PacketType::Form) {
-      pContainerNode = ToNode(GetXFAObject())->GetContainerNode();
+    } else if (GetXFANode()->GetPacketType() == XFA_PacketType::Form) {
+      pContainerNode = GetXFANode()->GetContainerNode();
     }
 
     if (pContainerNode)
@@ -1403,52 +1331,55 @@
   WideString content = GetContent(true);
   if (content.IsEmpty() && eType != XFA_Element::Text &&
       eType != XFA_Element::SubmitUrl) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
   } else if (eType == XFA_Element::Integer) {
-    pValue->SetInteger(FXSYS_wtoi(content.c_str()));
+    *pValue = fxv8::NewNumberHelper(pIsolate, FXSYS_wtoi(content.c_str()));
   } else if (eType == XFA_Element::Float || eType == XFA_Element::Decimal) {
     CFGAS_Decimal decimal(content.AsStringView());
-    pValue->SetFloat(decimal.ToFloat());
+    *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat());
   } else {
-    pValue->SetString(content.ToUTF8().AsStringView());
+    *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
   }
 }
 
-void CJX_Object::ScriptSomDefaultValue_Read(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomDefaultValue_Read(v8::Isolate* pIsolate,
+                                            v8::Local<v8::Value>* pValue,
                                             bool bSetting,
                                             XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   WideString content = GetContent(true);
   if (content.IsEmpty()) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
     return;
   }
-  pValue->SetString(content.ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
 }
 
-void CJX_Object::ScriptSomDataNode(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomDataNode(v8::Isolate* pIsolate,
+                                   v8::Local<v8::Value>* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  CXFA_Node* pDataNode = ToNode(GetXFAObject())->GetBindData();
+  CXFA_Node* pDataNode = GetXFANode()->GetBindData();
   if (!pDataNode) {
-    pValue->SetNull();
+    *pValue = fxv8::NewNullHelper(pIsolate);
     return;
   }
 
-  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-      pDataNode));
+  *pValue =
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pDataNode);
 }
 
-void CJX_Object::ScriptSomMandatory(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomMandatory(v8::Isolate* pIsolate,
+                                    v8::Local<v8::Value>* pValue,
                                     bool bSetting,
                                     XFA_Attribute eAttribute) {
   CXFA_Validate* validate =
@@ -1457,25 +1388,28 @@
     return;
 
   if (bSetting) {
-    validate->SetNullTest(pValue->ToWideString());
+    validate->SetNullTest(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
     return;
   }
 
-  pValue->SetString(XFA_AttributeValueToName(validate->GetNullTest()));
+  *pValue = fxv8::NewStringHelper(
+      pIsolate, XFA_AttributeValueToName(validate->GetNullTest()));
 }
 
-void CJX_Object::ScriptSomInstanceIndex(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSomInstanceIndex(v8::Isolate* pIsolate,
+                                        v8::Local<v8::Value>* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {
   if (!bSetting) {
-    pValue->SetInteger(Subform_and_SubformSet_InstanceIndex());
+    *pValue =
+        fxv8::NewNumberHelper(pIsolate, Subform_and_SubformSet_InstanceIndex());
     return;
   }
 
-  int32_t iTo = pValue->ToInteger();
+  int32_t iTo = fxv8::ReentrantToInt32Helper(pIsolate, *pValue);
   int32_t iFrom = Subform_and_SubformSet_InstanceIndex();
   CXFA_Node* pManagerNode = nullptr;
-  for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode;
+  for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode;
        pNode = pNode->GetPrevSibling()) {
     if (pNode->GetElementType() == XFA_Element::InstanceManager) {
       pManagerNode = pNode;
@@ -1486,23 +1420,31 @@
     return;
 
   auto* mgr = static_cast<CJX_InstanceManager*>(pManagerNode->JSObject());
-  mgr->MoveInstance(iTo, iFrom);
+  mgr->MoveInstance(pIsolate, iTo, iFrom);
   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
   if (!pNotify)
     return;
 
-  CXFA_Node* pToInstance = pManagerNode->GetItemIfExists(iTo);
-  if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform) {
+  auto* pToInstance =
+      CXFA_Subform::FromNode(pManagerNode->GetItemIfExists(iTo));
+  if (pToInstance)
     pNotify->RunSubformIndexChange(pToInstance);
-  }
 
-  CXFA_Node* pFromInstance = pManagerNode->GetItemIfExists(iFrom);
-  if (pFromInstance &&
-      pFromInstance->GetElementType() == XFA_Element::Subform) {
+  auto* pFromInstance =
+      CXFA_Subform::FromNode(pManagerNode->GetItemIfExists(iFrom));
+  if (pFromInstance)
     pNotify->RunSubformIndexChange(pFromInstance);
-  }
 }
 
-void CJX_Object::ScriptSubmitFormatMode(CFXJSE_Value* pValue,
+void CJX_Object::ScriptSubmitFormatMode(v8::Isolate* pIsolate,
+                                        v8::Local<v8::Value>* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {}
+
+CJX_Object::CalcData::CalcData() = default;
+
+CJX_Object::CalcData::~CalcData() = default;
+
+void CJX_Object::CalcData::Trace(cppgc::Visitor* visitor) const {
+  ContainerTrace(visitor, m_Globals);
+}
diff --git a/fxjs/xfa/cjx_object.h b/fxjs/xfa/cjx_object.h
index 0633b5f..caa1a5e 100644
--- a/fxjs/xfa/cjx_object.h
+++ b/fxjs/xfa/cjx_object.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,53 +9,41 @@
 
 #include <map>
 #include <memory>
-#include <utility>
 #include <vector>
 
-#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/widestring.h"
+#include "fxjs/gc/heap.h"
+#include "fxjs/xfa/fxjse.h"
 #include "fxjs/xfa/jse_define.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/base/span.h"
+#include "v8/include/cppgc/garbage-collected.h"
+#include "v8/include/cppgc/member.h"
+#include "v8/include/v8-forward.h"
 #include "xfa/fxfa/fxfa_basic.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
 
+class CFXJSE_Engine;
+class CFXJSE_MapModule;
 class CFX_XMLElement;
-class CFXJSE_Value;
-class CFX_V8;
 class CJX_Object;
-class CXFA_CalcData;
 class CXFA_Document;
 class CXFA_LayoutItem;
+class CXFA_Measurement;
 class CXFA_Node;
 class CXFA_Object;
-struct XFA_MAPMODULEDATA;
 
-typedef CJS_Result (*CJX_MethodCall)(
-    CJX_Object* obj,
-    CFX_V8* runtime,
-    const std::vector<v8::Local<v8::Value>>& params);
+using CJX_MethodCall =
+    CJS_Result (*)(CJX_Object* obj,
+                   CFXJSE_Engine* runtime,
+                   const std::vector<v8::Local<v8::Value>>& params);
 
 struct CJX_MethodSpec {
   const char* pName;
   CJX_MethodCall pMethodCall;
 };
 
-typedef void (*PD_CALLBACK_FREEDATA)(void* pData);
-typedef void (*PD_CALLBACK_DUPLICATEDATA)(void*& pData);
-
-struct XFA_MAPDATABLOCKCALLBACKINFO {
-  PD_CALLBACK_FREEDATA pFree;
-  PD_CALLBACK_DUPLICATEDATA pCopy;
-};
-
-enum XFA_SOM_MESSAGETYPE {
-  XFA_SOM_ValidationMessage,
-  XFA_SOM_FormatMessage,
-  XFA_SOM_MandatoryMessage
-};
-
-class CJX_Object {
+class CJX_Object : public cppgc::GarbageCollected<CJX_Object>,
+                   public CFXJSE_HostObject {
  public:
   // Corresponds 1:1 with CJX_ subclasses.
   enum class TypeTag {
@@ -96,51 +84,66 @@
     Xfa,
   };
 
-  explicit CJX_Object(CXFA_Object* obj);
-  virtual ~CJX_Object();
+  class CalcData : public cppgc::GarbageCollected<CalcData> {
+   public:
+    CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+    ~CalcData();
 
+    void Trace(cppgc::Visitor* visitor) const;
+
+    std::vector<cppgc::Member<CXFA_Node>> m_Globals;
+
+   private:
+    CalcData();
+  };
+
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
+  ~CJX_Object() override;
+
+  // CFXJSE_HostObject:
+  CJX_Object* AsCJXObject() override;
+
+  virtual void Trace(cppgc::Visitor* visitor) const;
   virtual bool DynamicTypeIs(TypeTag eType) const;
 
   JSE_PROP(className);
 
   CXFA_Document* GetDocument() const;
-  CXFA_Object* GetXFAObject() const { return object_.Get(); }
+  CXFA_Node* GetXFANode() const;
+  CXFA_Object* GetXFAObject() const { return object_; }
 
   void SetCalcRecursionCount(size_t count) { calc_recursion_count_ = count; }
   size_t GetCalcRecursionCount() const { return calc_recursion_count_; }
 
-  void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_.Reset(item); }
-  CXFA_LayoutItem* GetLayoutItem() const { return layout_item_.Get(); }
+  void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_ = item; }
+  CXFA_LayoutItem* GetLayoutItem() const { return layout_item_; }
 
   bool HasMethod(const WideString& func) const;
   CJS_Result RunMethod(const WideString& func,
                        const std::vector<v8::Local<v8::Value>>& params);
 
-  bool HasAttribute(XFA_Attribute eAttr);
-  void SetAttribute(XFA_Attribute eAttr, WideStringView wsValue, bool bNotify);
-  void SetAttribute(WideStringView wsAttr,
-                    WideStringView wsValue,
-                    bool bNotify);
+  bool HasAttribute(XFA_Attribute eAttr) const;
+  WideString GetAttributeByString(WideStringView attr) const;
+  WideString GetAttributeByEnum(XFA_Attribute attr) const;
+  absl::optional<WideString> TryAttribute(XFA_Attribute eAttr,
+                                          bool bUseDefault) const;
+  void SetAttributeByEnum(XFA_Attribute eAttr,
+                          const WideString& wsValue,
+                          bool bNotify);
+  void SetAttributeByString(WideStringView wsAttr, const WideString& wsValue);
   void RemoveAttribute(WideStringView wsAttr);
-  WideString GetAttribute(WideStringView attr);
-  WideString GetAttribute(XFA_Attribute attr);
-  Optional<WideString> TryAttribute(WideStringView wsAttr, bool bUseDefault);
-  Optional<WideString> TryAttribute(XFA_Attribute eAttr, bool bUseDefault);
 
-  Optional<WideString> TryContent(bool bScriptModify, bool bProto);
+  WideString GetContent(bool bScriptModify) const;
+  absl::optional<WideString> TryContent(bool bScriptModify, bool bProto) const;
   void SetContent(const WideString& wsContent,
                   const WideString& wsXMLValue,
                   bool bNotify,
                   bool bScriptModify,
                   bool bSyncData);
-  WideString GetContent(bool bScriptModify);
 
   template <typename T>
   T* GetProperty(int32_t index, XFA_Element eType) const {
-    CXFA_Node* node;
-    int32_t count;
-    std::tie(node, count) = GetPropertyInternal(index, eType);
-    return static_cast<T*>(node);
+    return static_cast<T*>(GetPropertyInternal(index, eType));
   }
   template <typename T>
   T* GetOrCreateProperty(int32_t index, XFA_Element eType) {
@@ -148,9 +151,7 @@
   }
 
   void SetAttributeValue(const WideString& wsValue,
-                         const WideString& wsXMLValue,
-                         bool bNotify,
-                         bool bScriptModify);
+                         const WideString& wsXMLValue);
 
   // Not actual properties, but invoked as property handlers to cover
   // a broad range of underlying properties.
@@ -170,105 +171,113 @@
   JSE_PROP(ScriptSomInstanceIndex);
   JSE_PROP(ScriptSubmitFormatMode);
 
-  void ScriptSomMessage(CFXJSE_Value* pValue,
-                        bool bSetting,
-                        XFA_SOM_MESSAGETYPE iMessageType);
+  absl::optional<WideString> TryNamespace() const;
 
-  Optional<WideString> TryNamespace();
-
-  Optional<int32_t> TryInteger(XFA_Attribute eAttr, bool bUseDefault) const;
-  void SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify);
   int32_t GetInteger(XFA_Attribute eAttr) const;
+  absl::optional<int32_t> TryInteger(XFA_Attribute eAttr,
+                                     bool bUseDefault) const;
+  void SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify);
 
-  Optional<WideString> TryCData(XFA_Attribute eAttr, bool bUseDefault) const;
-  void SetCData(XFA_Attribute eAttr,
-                const WideString& wsValue,
-                bool bNotify,
-                bool bScriptModify);
   WideString GetCData(XFA_Attribute eAttr) const;
+  absl::optional<WideString> TryCData(XFA_Attribute eAttr,
+                                      bool bUseDefault) const;
+  void SetCData(XFA_Attribute eAttr, const WideString& wsValue);
 
-  Optional<XFA_AttributeValue> TryEnum(XFA_Attribute eAttr,
-                                       bool bUseDefault) const;
-  void SetEnum(XFA_Attribute eAttr, XFA_AttributeValue eValue, bool bNotify);
   XFA_AttributeValue GetEnum(XFA_Attribute eAttr) const;
+  absl::optional<XFA_AttributeValue> TryEnum(XFA_Attribute eAttr,
+                                             bool bUseDefault) const;
+  void SetEnum(XFA_Attribute eAttr, XFA_AttributeValue eValue, bool bNotify);
 
-  Optional<bool> TryBoolean(XFA_Attribute eAttr, bool bUseDefault);
+  bool GetBoolean(XFA_Attribute eAttr) const;
+  absl::optional<bool> TryBoolean(XFA_Attribute eAttr, bool bUseDefault) const;
   void SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify);
-  bool GetBoolean(XFA_Attribute eAttr);
 
-  Optional<CXFA_Measurement> TryMeasure(XFA_Attribute eAttr,
-                                        bool bUseDefault) const;
-  Optional<float> TryMeasureAsFloat(XFA_Attribute attr) const;
-  void SetMeasure(XFA_Attribute eAttr, CXFA_Measurement mValue, bool bNotify);
   CXFA_Measurement GetMeasure(XFA_Attribute eAttr) const;
   float GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const;
+  absl::optional<CXFA_Measurement> TryMeasure(XFA_Attribute eAttr,
+                                              bool bUseDefault) const;
+  absl::optional<float> TryMeasureAsFloat(XFA_Attribute attr) const;
+  void SetMeasure(XFA_Attribute eAttr,
+                  const CXFA_Measurement& mValue,
+                  bool bNotify);
 
-  void MergeAllData(CXFA_Object* pDstModule);
+  void MergeAllData(CXFA_Object* pDstObj);
 
-  void SetCalcData(std::unique_ptr<CXFA_CalcData> data);
-  CXFA_CalcData* GetCalcData() const { return calc_data_.get(); }
-  std::unique_ptr<CXFA_CalcData> ReleaseCalcData();
+  CalcData* GetCalcData() const { return calc_data_; }
+  CalcData* GetOrCreateCalcData(cppgc::Heap* heap);
+  void TakeCalcDataFrom(CJX_Object* that);
 
-  int32_t InstanceManager_SetInstances(int32_t iDesired);
-  int32_t InstanceManager_MoveInstance(int32_t iTo, int32_t iFrom);
-
-  void ThrowInvalidPropertyException() const;
-  void ThrowArgumentMismatchException() const;
-  void ThrowIndexOutOfBoundsException() const;
-  void ThrowParamCountMismatchException(const WideString& method) const;
-  void ThrowTooManyOccurancesException(const WideString& obj) const;
+  void ThrowInvalidPropertyException(v8::Isolate* pIsolate) const;
+  void ThrowArgumentMismatchException(v8::Isolate* pIsolate) const;
+  void ThrowIndexOutOfBoundsException(v8::Isolate* pIsolate) const;
+  void ThrowParamCountMismatchException(v8::Isolate* pIsolate,
+                                        const WideString& method) const;
+  void ThrowTooManyOccurrencesException(v8::Isolate* pIsolate,
+                                        const WideString& obj) const;
 
  protected:
+  enum class SOMMessageType {
+    kValidationMessage,
+    kFormatMessage,
+    kMandatoryMessage
+  };
+
+  explicit CJX_Object(CXFA_Object* obj);
+
+  void ScriptSomMessage(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
+                        bool bSetting,
+                        SOMMessageType iMessageType);
+  void SetAttributeValueImpl(const WideString& wsValue,
+                             const WideString& wsXMLValue,
+                             bool bNotify,
+                             bool bScriptModify);
+  void SetCDataImpl(XFA_Attribute eAttr,
+                    const WideString& wsValue,
+                    bool bNotify,
+                    bool bScriptModify);
   void DefineMethods(pdfium::span<const CJX_MethodSpec> methods);
-  void MoveBufferMapData(CXFA_Object* pSrcModule, CXFA_Object* pDstModule);
-  void SetMapModuleString(void* pKey, WideStringView wsValue);
-  void ThrowException(const WideString& str) const;
+  void MoveBufferMapData(CXFA_Object* pSrcObj, CXFA_Object* pDstObj);
+  void ThrowException(v8::Isolate* pIsolate, const WideString& str) const;
 
  private:
   using Type__ = CJX_Object;
   static const TypeTag static_type__ = TypeTag::Object;
 
-  std::pair<CXFA_Node*, int32_t> GetPropertyInternal(int32_t index,
-                                                     XFA_Element eType) const;
+  CXFA_Node* GetPropertyInternal(int32_t index, XFA_Element eType) const;
   CXFA_Node* GetOrCreatePropertyInternal(int32_t index, XFA_Element eType);
 
-  void OnChanged(XFA_Attribute eAttr, bool bNotify, bool bScriptModify);
-  void OnChanging(XFA_Attribute eAttr, bool bNotify);
-  void SetUserData(void* pKey,
-                   void* pData,
-                   const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
+  void OnChanging(XFA_Attribute eAttr);
+  void OnChanged(XFA_Attribute eAttr, bool bScriptModify);
 
   // Returns a pointer to the XML node that needs to be updated with the new
   // attribute value. |nullptr| if no update is needed.
-  CFX_XMLElement* SetValue(XFA_Attribute eAttr, void* pValue, bool bNotify);
+  CFX_XMLElement* SetValue(XFA_Attribute eAttr, int32_t value, bool bNotify);
   int32_t Subform_and_SubformSet_InstanceIndex();
 
-  XFA_MAPMODULEDATA* CreateMapModuleData();
-  XFA_MAPMODULEDATA* GetMapModuleData() const;
-  void SetMapModuleValue(void* pKey, void* pValue);
-  Optional<void*> GetMapModuleValue(void* pKey) const;
-  Optional<WideString> GetMapModuleString(void* pKey) const;
-  void SetMapModuleBuffer(void* pKey,
-                          void* pValue,
-                          int32_t iBytes,
-                          const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
-  bool GetMapModuleBuffer(void* pKey, void** pValue, int32_t* pBytes) const;
-  bool HasMapModuleKey(void* pKey);
-  void ClearMapModuleBuffer();
-  void RemoveMapModuleKey(void* pKey);
-  void MoveBufferMapData(CXFA_Object* pDstModule);
+  CFXJSE_MapModule* CreateMapModule();
+  CFXJSE_MapModule* GetMapModule() const;
+  void SetMapModuleValue(uint32_t key, int32_t value);
+  void SetMapModuleString(uint32_t key, const WideString& wsValue);
+  void SetMapModuleMeasurement(uint32_t key, const CXFA_Measurement& value);
+  absl::optional<int32_t> GetMapModuleValue(uint32_t key) const;
+  absl::optional<WideString> GetMapModuleString(uint32_t key) const;
+  absl::optional<CXFA_Measurement> GetMapModuleMeasurement(uint32_t key) const;
+  absl::optional<int32_t> GetMapModuleValueFollowingChain(uint32_t key) const;
+  absl::optional<WideString> GetMapModuleStringFollowingChain(
+      uint32_t key) const;
+  absl::optional<CXFA_Measurement> GetMapModuleMeasurementFollowingChain(
+      uint32_t key) const;
+  bool HasMapModuleKey(uint32_t key) const;
+  void RemoveMapModuleKey(uint32_t key);
+  void MoveBufferMapData(CXFA_Object* pDstObj);
 
-  UnownedPtr<CXFA_Object> object_;
-  UnownedPtr<CXFA_LayoutItem> layout_item_;
-  std::unique_ptr<XFA_MAPMODULEDATA> map_module_data_;
-  std::unique_ptr<CXFA_CalcData> calc_data_;
+  cppgc::Member<CXFA_Object> object_;
+  cppgc::Member<CXFA_LayoutItem> layout_item_;
+  cppgc::Member<CalcData> calc_data_;
+  std::unique_ptr<CFXJSE_MapModule> map_module_;
   std::map<ByteString, CJX_MethodCall> method_specs_;
   size_t calc_recursion_count_ = 0;
 };
 
-typedef void (*XFA_ATTRIBUTE_CALLBACK)(CJX_Object* pNode,
-                                       CFXJSE_Value* pValue,
-                                       bool bSetting,
-                                       XFA_Attribute eAttribute);
-
 #endif  // FXJS_XFA_CJX_OBJECT_H_
diff --git a/fxjs/xfa/cjx_object_embeddertest.cpp b/fxjs/xfa/cjx_object_embeddertest.cpp
new file mode 100644
index 0000000..b433665
--- /dev/null
+++ b/fxjs/xfa/cjx_object_embeddertest.cpp
@@ -0,0 +1,20 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/xfa/cjx_object.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/xfa_js_embedder_test.h"
+
+class CJX_ObjectEmbedderTest : public XFAJSEmbedderTest {};
+
+// Should not crash, but document is not valid.
+TEST_F(CJX_ObjectEmbedderTest, Bug1327884) {
+  ASSERT_FALSE(OpenDocument("bug_1327884.pdf"));
+}
+
+// Should not CHECK(), but document is uninteresting.
+TEST_F(CJX_ObjectEmbedderTest, Bug1333298) {
+  ASSERT_TRUE(OpenDocument("bug_1333298.pdf"));
+}
diff --git a/fxjs/xfa/cjx_occur.cpp b/fxjs/xfa/cjx_occur.cpp
index de8942d..657b230 100644
--- a/fxjs/xfa/cjx_occur.cpp
+++ b/fxjs/xfa/cjx_occur.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,9 +6,9 @@
 
 #include "fxjs/xfa/cjx_occur.h"
 
-#include <algorithm>
-
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_occur.h"
 
 CJX_Occur::CJX_Occur(CXFA_Occur* node) : CJX_Node(node) {}
@@ -19,24 +19,27 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Occur::max(CFXJSE_Value* pValue,
+void CJX_Occur::max(v8::Isolate* pIsolate,
+                    v8::Local<v8::Value>* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {
   CXFA_Occur* occur = static_cast<CXFA_Occur*>(GetXFANode());
   if (!bSetting) {
-    pValue->SetInteger(occur->GetMax());
+    *pValue = fxv8::NewNumberHelper(pIsolate, occur->GetMax());
     return;
   }
-  occur->SetMax(pValue->ToInteger());
+  occur->SetMax(fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
 }
 
-void CJX_Occur::min(CFXJSE_Value* pValue,
+// NOLINTNEXTLINE(build/include_what_you_use)
+void CJX_Occur::min(v8::Isolate* pIsolate,
+                    v8::Local<v8::Value>* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {
   CXFA_Occur* occur = static_cast<CXFA_Occur*>(GetXFANode());
   if (!bSetting) {
-    pValue->SetInteger(occur->GetMin());
+    *pValue = fxv8::NewNumberHelper(pIsolate, occur->GetMin());
     return;
   }
-  occur->SetMin(pValue->ToInteger());
+  occur->SetMin(fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
 }
diff --git a/fxjs/xfa/cjx_occur.h b/fxjs/xfa/cjx_occur.h
index 8b912b6..06887dd 100644
--- a/fxjs/xfa/cjx_occur.h
+++ b/fxjs/xfa/cjx_occur.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Occur final : public CJX_Node {
  public:
-  explicit CJX_Occur(CXFA_Occur* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Occur() override;
 
   // CJX_Object:
@@ -24,6 +24,8 @@
   JSE_PROP(min);
 
  private:
+  explicit CJX_Occur(CXFA_Occur* node);
+
   using Type__ = CJX_Occur;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_packet.cpp b/fxjs/xfa/cjx_packet.cpp
index 2c8b67d..e77f165 100644
--- a/fxjs/xfa/cjx_packet.cpp
+++ b/fxjs/xfa/cjx_packet.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,10 +10,12 @@
 #include <vector>
 
 #include "core/fxcrt/xml/cfx_xmldocument.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmltext.h"
 #include "fxjs/cfx_v8.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/cxfa_ffdoc.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/parser/cxfa_packet.h"
@@ -27,14 +29,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Packet::~CJX_Packet() {}
+CJX_Packet::~CJX_Packet() = default;
 
 bool CJX_Packet::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Packet::getAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -49,7 +51,7 @@
 }
 
 CJS_Result CJX_Packet::setAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 2)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -63,21 +65,20 @@
 }
 
 CJS_Result CJX_Packet::removeAttribute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CFX_XMLElement* pElement = ToXMLElement(GetXFANode()->GetXMLMappingNode());
-  if (pElement) {
-    WideString name = runtime->ToWideString(params[0]);
-    if (pElement->HasAttribute(name))
-      pElement->RemoveAttribute(name);
-  }
+  if (pElement)
+    pElement->RemoveAttribute(runtime->ToWideString(params[0]));
+
   return CJS_Result::Success(runtime->NewNull());
 }
 
-void CJX_Packet::content(CFXJSE_Value* pValue,
+void CJX_Packet::content(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
   CFX_XMLElement* element = ToXMLElement(GetXFANode()->GetXMLMappingNode());
@@ -87,9 +88,10 @@
           GetXFANode()
               ->GetDocument()
               ->GetNotify()
-              ->GetHDOC()
+              ->GetFFDoc()
               ->GetXMLDocument()
-              ->CreateNode<CFX_XMLText>(pValue->ToWideString()));
+              ->CreateNode<CFX_XMLText>(
+                  fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)));
     }
     return;
   }
@@ -98,5 +100,5 @@
   if (element)
     wsTextData = element->GetTextData();
 
-  pValue->SetString(wsTextData.ToUTF8().AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, wsTextData.ToUTF8().AsStringView());
 }
diff --git a/fxjs/xfa/cjx_packet.h b/fxjs/xfa/cjx_packet.h
index df4a987..4b4687c 100644
--- a/fxjs/xfa/cjx_packet.h
+++ b/fxjs/xfa/cjx_packet.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Packet final : public CJX_Node {
  public:
-  explicit CJX_Packet(CXFA_Packet* packet);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Packet() override;
 
   // CJX_Object:
@@ -27,6 +27,8 @@
   JSE_PROP(content);
 
  private:
+  explicit CJX_Packet(CXFA_Packet* packet);
+
   using Type__ = CJX_Packet;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_script.cpp b/fxjs/xfa/cjx_script.cpp
index 482a57e..2c02b2b 100644
--- a/fxjs/xfa/cjx_script.cpp
+++ b/fxjs/xfa/cjx_script.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,8 @@
 
 #include "fxjs/xfa/cjx_script.h"
 
-#include "fxjs/xfa/cfxjse_value.h"
+#include "fxjs/fxv8.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_script.h"
 
 CJX_Script::CJX_Script(CXFA_Script* node) : CJX_Node(node) {}
@@ -17,12 +18,13 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Script::stateless(CFXJSE_Value* pValue,
+void CJX_Script::stateless(v8::Isolate* pIsolate,
+                           v8::Local<v8::Value>* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-  pValue->SetString(FX_UTF8Encode(WideStringView(L"0", 1)).AsStringView());
+  *pValue = fxv8::NewStringHelper(pIsolate, "0");
 }
diff --git a/fxjs/xfa/cjx_script.h b/fxjs/xfa/cjx_script.h
index af7c985..272ec2a 100644
--- a/fxjs/xfa/cjx_script.h
+++ b/fxjs/xfa/cjx_script.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Script final : public CJX_Node {
  public:
-  explicit CJX_Script(CXFA_Script* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Script() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(stateless);
 
  private:
+  explicit CJX_Script(CXFA_Script* node);
+
   using Type__ = CJX_Script;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.cpp b/fxjs/xfa/cjx_signaturepseudomodel.cpp
index 97ecd6e..a740cb8 100644
--- a/fxjs/xfa/cjx_signaturepseudomodel.cpp
+++ b/fxjs/xfa/cjx_signaturepseudomodel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,9 +8,10 @@
 
 #include <vector>
 
-#include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
 
 const CJX_MethodSpec CJX_SignaturePseudoModel::MethodSpecs[] = {
@@ -25,14 +26,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() {}
+CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() = default;
 
 bool CJX_SignaturePseudoModel::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_SignaturePseudoModel::verifySignature(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 4)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -41,7 +42,7 @@
 }
 
 CJS_Result CJX_SignaturePseudoModel::sign(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() < 3 || params.size() > 7)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -50,7 +51,7 @@
 }
 
 CJS_Result CJX_SignaturePseudoModel::enumerate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -59,7 +60,7 @@
 }
 
 CJS_Result CJX_SignaturePseudoModel::clear(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.empty() || params.size() > 2)
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.h b/fxjs/xfa/cjx_signaturepseudomodel.h
index ed77238..6cf3460 100644
--- a/fxjs/xfa/cjx_signaturepseudomodel.h
+++ b/fxjs/xfa/cjx_signaturepseudomodel.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_SignaturePseudoModel final : public CJX_Object {
  public:
-  explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_SignaturePseudoModel() override;
 
   // CJX_Object:
@@ -26,6 +26,8 @@
   JSE_METHOD(clear);
 
  private:
+  explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model);
+
   using Type__ = CJX_SignaturePseudoModel;
   using ParentType__ = CJX_Object;
 
diff --git a/fxjs/xfa/cjx_source.cpp b/fxjs/xfa/cjx_source.cpp
index f6d3187..33a11ff 100644
--- a/fxjs/xfa/cjx_source.cpp
+++ b/fxjs/xfa/cjx_source.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -35,13 +35,13 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Source::~CJX_Source() {}
+CJX_Source::~CJX_Source() = default;
 
 bool CJX_Source::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-CJS_Result CJX_Source::next(CFX_V8* runtime,
+CJS_Result CJX_Source::next(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -50,7 +50,7 @@
 }
 
 CJS_Result CJX_Source::cancelBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -58,7 +58,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::first(CFX_V8* runtime,
+CJS_Result CJX_Source::first(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -67,7 +67,7 @@
 }
 
 CJS_Result CJX_Source::updateBatch(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -76,7 +76,7 @@
 }
 
 CJS_Result CJX_Source::previous(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -84,7 +84,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::isBOF(CFX_V8* runtime,
+CJS_Result CJX_Source::isBOF(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -92,7 +92,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::isEOF(CFX_V8* runtime,
+CJS_Result CJX_Source::isEOF(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -100,7 +100,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::cancel(CFX_V8* runtime,
+CJS_Result CJX_Source::cancel(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -108,7 +108,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::update(CFX_V8* runtime,
+CJS_Result CJX_Source::update(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -116,7 +116,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::open(CFX_V8* runtime,
+CJS_Result CJX_Source::open(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -125,7 +125,7 @@
 }
 
 CJS_Result CJX_Source::deleteItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -133,7 +133,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::addNew(CFX_V8* runtime,
+CJS_Result CJX_Source::addNew(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -142,7 +142,7 @@
 }
 
 CJS_Result CJX_Source::requery(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -150,7 +150,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::resync(CFX_V8* runtime,
+CJS_Result CJX_Source::resync(CFXJSE_Engine* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -158,7 +158,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::close(CFX_V8* runtime,
+CJS_Result CJX_Source::close(CFXJSE_Engine* runtime,
                              const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -166,7 +166,7 @@
   return CJS_Result::Success();
 }
 
-CJS_Result CJX_Source::last(CFX_V8* runtime,
+CJS_Result CJX_Source::last(CFXJSE_Engine* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -175,7 +175,7 @@
 }
 
 CJS_Result CJX_Source::hasDataChanged(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -183,6 +183,7 @@
   return CJS_Result::Success();
 }
 
-void CJX_Source::db(CFXJSE_Value* pValue,
+void CJX_Source::db(v8::Isolate* pIsolate,
+                    v8::Local<v8::Value>* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_source.h b/fxjs/xfa/cjx_source.h
index cd70601..a3e7fda 100644
--- a/fxjs/xfa/cjx_source.h
+++ b/fxjs/xfa/cjx_source.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Source final : public CJX_Node {
  public:
-  explicit CJX_Source(CXFA_Source* src);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Source() override;
 
   // CJX_Object:
@@ -41,6 +41,8 @@
   JSE_PROP(db);
 
  private:
+  explicit CJX_Source(CXFA_Source* src);
+
   using Type__ = CJX_Source;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_subform.cpp b/fxjs/xfa/cjx_subform.cpp
index 8a72b5b..5a4efc0 100644
--- a/fxjs/xfa/cjx_subform.cpp
+++ b/fxjs/xfa/cjx_subform.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,9 +9,10 @@
 #include <vector>
 
 #include "fxjs/cfx_v8.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
 #include "xfa/fxfa/fxfa.h"
@@ -28,14 +29,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Subform::~CJX_Subform() {}
+CJX_Subform::~CJX_Subform() = default;
 
 bool CJX_Subform::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Subform::execEvent(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -46,7 +47,7 @@
 }
 
 CJS_Result CJX_Subform::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -59,7 +60,7 @@
 }
 
 CJS_Result CJX_Subform::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -72,7 +73,7 @@
 }
 
 CJS_Result CJX_Subform::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -87,29 +88,34 @@
       runtime->NewBoolean(iRet != XFA_EventError::kError));
 }
 
-void CJX_Subform::locale(CFXJSE_Value* pValue,
+void CJX_Subform::locale(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
   if (bSetting) {
-    SetCData(XFA_Attribute::Locale, pValue->ToWideString(), true, true);
+    SetCDataImpl(XFA_Attribute::Locale,
+                 fxv8::ReentrantToWideStringHelper(pIsolate, *pValue), true,
+                 true);
     return;
   }
 
   WideString wsLocaleName = GetXFANode()->GetLocaleName().value_or(L"");
-  pValue->SetString(wsLocaleName.ToUTF8().AsStringView());
+  *pValue =
+      fxv8::NewStringHelper(pIsolate, wsLocaleName.ToUTF8().AsStringView());
 }
 
-void CJX_Subform::instanceManager(CFXJSE_Value* pValue,
+void CJX_Subform::instanceManager(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Value>* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
   WideString wsName = GetCData(XFA_Attribute::Name);
   CXFA_Node* pInstanceMgr = nullptr;
-  for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode;
+  for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode;
        pNode = pNode->GetPrevSibling()) {
     if (pNode->GetElementType() == XFA_Element::InstanceManager) {
       WideString wsInstMgrName =
@@ -121,11 +127,9 @@
       break;
     }
   }
-  if (!pInstanceMgr) {
-    pValue->SetNull();
-    return;
-  }
-
-  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-      pInstanceMgr));
+  *pValue = pInstanceMgr ? GetDocument()
+                               ->GetScriptContext()
+                               ->GetOrCreateJSBindingFromMap(pInstanceMgr)
+                               .As<v8::Value>()
+                         : fxv8::NewNullHelper(pIsolate).As<v8::Value>();
 }
diff --git a/fxjs/xfa/cjx_subform.h b/fxjs/xfa/cjx_subform.h
index 83ac66e..c33712f 100644
--- a/fxjs/xfa/cjx_subform.h
+++ b/fxjs/xfa/cjx_subform.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,11 +10,11 @@
 #include "fxjs/xfa/cjx_container.h"
 #include "fxjs/xfa/jse_define.h"
 
-class CXFA_Delta;
+class CXFA_Node;
 
 class CJX_Subform final : public CJX_Container {
  public:
-  explicit CJX_Subform(CXFA_Node* container);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Subform() override;
 
   // CJX_Object:
@@ -29,6 +29,8 @@
   JSE_PROP(locale);
 
  private:
+  explicit CJX_Subform(CXFA_Node* container);
+
   using Type__ = CJX_Subform;
   using ParentType__ = CJX_Container;
 
diff --git a/fxjs/xfa/cjx_template.cpp b/fxjs/xfa/cjx_template.cpp
index b3d9d90..e3779e3 100644
--- a/fxjs/xfa/cjx_template.cpp
+++ b/fxjs/xfa/cjx_template.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_template.h"
 
@@ -26,14 +27,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Template::~CJX_Template() {}
+CJX_Template::~CJX_Template() = default;
 
 bool CJX_Template::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Template::formNodes(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -42,17 +43,17 @@
 }
 
 CJS_Result CJX_Template::remerge(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  GetDocument()->DoDataRemerge(true);
+  GetDocument()->DoDataRemerge();
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_Template::execInitialize(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -62,7 +63,7 @@
 }
 
 CJS_Result CJX_Template::recalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -71,7 +72,7 @@
 }
 
 CJS_Result CJX_Template::execCalculate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -81,7 +82,7 @@
 }
 
 CJS_Result CJX_Template::execValidate(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_template.h b/fxjs/xfa/cjx_template.h
index d396459..caf1b74 100644
--- a/fxjs/xfa/cjx_template.h
+++ b/fxjs/xfa/cjx_template.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Template final : public CJX_Model {
  public:
-  explicit CJX_Template(CXFA_Template* tmpl);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Template() override;
 
   // CJX_Object:
@@ -31,6 +31,8 @@
   JSE_METHOD(remerge);
 
  private:
+  explicit CJX_Template(CXFA_Template* tmpl);
+
   using Type__ = CJX_Template;
   using ParentType__ = CJX_Model;
 
diff --git a/fxjs/xfa/cjx_textnode.cpp b/fxjs/xfa/cjx_textnode.cpp
index 756a71d..2bdb414 100644
--- a/fxjs/xfa/cjx_textnode.cpp
+++ b/fxjs/xfa/cjx_textnode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,20 +11,22 @@
 
 CJX_TextNode::CJX_TextNode(CXFA_Node* node) : CJX_Node(node) {}
 
-CJX_TextNode::~CJX_TextNode() {}
+CJX_TextNode::~CJX_TextNode() = default;
 
 bool CJX_TextNode::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_TextNode::defaultValue(CFXJSE_Value* pValue,
+void CJX_TextNode::defaultValue(v8::Isolate* pIsolate,
+                                v8::Local<v8::Value>* pValue,
                                 bool bSetting,
                                 XFA_Attribute attr) {
-  ScriptSomDefaultValue(pValue, bSetting, attr);
+  ScriptSomDefaultValue(pIsolate, pValue, bSetting, attr);
 }
 
-void CJX_TextNode::value(CFXJSE_Value* pValue,
+void CJX_TextNode::value(v8::Isolate* pIsolate,
+                         v8::Local<v8::Value>* pValue,
                          bool bSetting,
                          XFA_Attribute attr) {
-  ScriptSomDefaultValue(pValue, bSetting, attr);
+  ScriptSomDefaultValue(pIsolate, pValue, bSetting, attr);
 }
diff --git a/fxjs/xfa/cjx_textnode.h b/fxjs/xfa/cjx_textnode.h
index 6d74658..dbef713 100644
--- a/fxjs/xfa/cjx_textnode.h
+++ b/fxjs/xfa/cjx_textnode.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_TextNode : public CJX_Node {
  public:
-  explicit CJX_TextNode(CXFA_Node* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_TextNode() override;
 
   // CJX_Object:
@@ -23,6 +23,9 @@
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
+ protected:
+  explicit CJX_TextNode(CXFA_Node* node);
+
  private:
   using Type__ = CJX_TextNode;
   using ParentType__ = CJX_Node;
diff --git a/fxjs/xfa/cjx_tree.cpp b/fxjs/xfa/cjx_tree.cpp
index cc13b7a..ec562ce 100644
--- a/fxjs/xfa/cjx_tree.cpp
+++ b/fxjs/xfa/cjx_tree.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,16 +8,19 @@
 
 #include <vector>
 
+#include "fxjs/fxv8.h"
 #include "fxjs/js_resources.h"
+#include "fxjs/xfa/cfxjse_class.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "v8/include/cppgc/allocation.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
 #include "xfa/fxfa/parser/cxfa_attachnodelist.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_object.h"
-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
 
 const CJX_MethodSpec CJX_Tree::MethodSpecs[] = {
     {"resolveNode", resolveNode_static},
@@ -27,58 +30,56 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_Tree::~CJX_Tree() {}
+CJX_Tree::~CJX_Tree() = default;
 
 bool CJX_Tree::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_Tree::resolveNode(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
 
-  WideString expression = runtime->ToWideString(params[0]);
+  WideString wsExpression = runtime->ToWideString(params[0]);
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  CXFA_Object* refNode = GetXFAObject();
-  if (refNode->GetElementType() == XFA_Element::Xfa)
-    refNode = pScriptContext->GetThisObject();
+  CXFA_Object* pRefNode = GetXFAObject();
+  if (pRefNode->GetElementType() == XFA_Element::Xfa)
+    pRefNode = pScriptContext->GetThisObject();
 
-  uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-                    XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
-                    XFA_RESOLVENODE_Siblings;
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  if (!pScriptContext->ResolveObjects(ToNode(refNode),
-                                      expression.AsStringView(), &resolveNodeRS,
-                                      dwFlag, nullptr)) {
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      pScriptContext->ResolveObjects(
+          ToNode(pRefNode), wsExpression.AsStringView(),
+          Mask<XFA_ResolveFlag>{
+              XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
+              XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
+              XFA_ResolveFlag::kSiblings});
+  if (!maybeResult.has_value())
     return CJS_Result::Success(runtime->NewNull());
-  }
 
-  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    CXFA_Object* pObject = resolveNodeRS.objects.front().Get();
-    CFXJSE_Value* value =
-        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pObject);
-
+  if (maybeResult.value().type == CFXJSE_Engine::ResolveResult::Type::kNodes) {
     return CJS_Result::Success(
-        value->DirectGetValue().Get(runtime->GetIsolate()));
+        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+            maybeResult.value().objects.front().Get()));
   }
 
-  if (!resolveNodeRS.script_attribute.callback ||
-      resolveNodeRS.script_attribute.eValueType != XFA_ScriptType::Object) {
+  if (!maybeResult.value().script_attribute.callback ||
+      maybeResult.value().script_attribute.eValueType !=
+          XFA_ScriptType::Object) {
     return CJS_Result::Success(runtime->NewNull());
   }
 
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
-  CJX_Object* jsObject = resolveNodeRS.objects.front()->JSObject();
-  (*resolveNodeRS.script_attribute.callback)(
-      jsObject, pValue.get(), false, resolveNodeRS.script_attribute.attribute);
-  return CJS_Result::Success(
-      pValue->DirectGetValue().Get(runtime->GetIsolate()));
+  v8::Local<v8::Value> pValue;
+  CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
+  (*maybeResult.value().script_attribute.callback)(
+      runtime->GetIsolate(), jsObject, &pValue, false,
+      maybeResult.value().script_attribute.attribute);
+  return CJS_Result::Success(pValue);
 }
 
 CJS_Result CJX_Tree::resolveNodes(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -88,146 +89,166 @@
     refNode = GetDocument()->GetScriptContext()->GetThisObject();
 
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
-  ResolveNodeList(pValue.get(), runtime->ToWideString(params[0]),
-                  XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
-                      XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
-                      XFA_RESOLVENODE_Siblings,
-                  ToNode(refNode));
-  return CJS_Result::Success(
-      pValue->DirectGetValue().Get(runtime->GetIsolate()));
+  const Mask<XFA_ResolveFlag> kFlags = {
+      XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
+      XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
+      XFA_ResolveFlag::kSiblings};
+  return CJS_Result::Success(ResolveNodeList(pScriptContext->GetIsolate(),
+                                             runtime->ToWideString(params[0]),
+                                             kFlags, ToNode(refNode)));
 }
 
-void CJX_Tree::all(CFXJSE_Value* pValue,
+void CJX_Tree::all(v8::Isolate* pIsolate,
+                   v8::Local<v8::Value>* pValue,
                    bool bSetting,
                    XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-
-  uint32_t dwFlag = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_ALL;
-  WideString wsExpression = GetAttribute(XFA_Attribute::Name) + L"[*]";
-  ResolveNodeList(pValue, wsExpression, dwFlag, nullptr);
+  const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kSiblings,
+                                        XFA_ResolveFlag::kALL};
+  WideString wsExpression = GetAttributeByEnum(XFA_Attribute::Name) + L"[*]";
+  *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr);
 }
 
-void CJX_Tree::classAll(CFXJSE_Value* pValue,
+void CJX_Tree::classAll(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
-
+  const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kSiblings,
+                                        XFA_ResolveFlag::kALL};
   WideString wsExpression =
       L"#" + WideString::FromASCII(GetXFAObject()->GetClassName()) + L"[*]";
-  ResolveNodeList(pValue, wsExpression,
-                  XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_ALL, nullptr);
+  *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr);
 }
 
-void CJX_Tree::nodes(CFXJSE_Value* pValue,
+void CJX_Tree::nodes(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pValue,
                      bool bSetting,
                      XFA_Attribute eAttribute) {
   if (bSetting) {
     WideString wsMessage = L"Unable to set ";
-    FXJSE_ThrowMessage(wsMessage.ToUTF8().AsStringView());
+    FXJSE_ThrowMessage(pIsolate, wsMessage.ToUTF8().AsStringView());
     return;
   }
 
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  CXFA_AttachNodeList* pNodeList =
-      new CXFA_AttachNodeList(GetDocument(), ToNode(GetXFAObject()));
-  pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass());
+  CXFA_Document* pDoc = GetDocument();
+  auto* pNodeList = cppgc::MakeGarbageCollected<CXFA_AttachNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc, GetXFANode());
+  pDoc->GetNodeOwner()->PersistList(pNodeList);
+
+  CFXJSE_Engine* pEngine = pDoc->GetScriptContext();
+  *pValue = pNodeList->JSObject()->NewBoundV8Object(
+      pIsolate, pEngine->GetJseNormalClass()->GetTemplate(pIsolate));
 }
 
-void CJX_Tree::parent(CFXJSE_Value* pValue,
+void CJX_Tree::parent(v8::Isolate* pIsolate,
+                      v8::Local<v8::Value>* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  CXFA_Node* pParent = ToNode(GetXFAObject())->GetParent();
-  if (!pParent) {
-    pValue->SetNull();
-    return;
-  }
-
-  pValue->Assign(
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pParent));
+  CXFA_Node* pParent = GetXFANode()->GetParent();
+  *pValue = pParent ? GetDocument()
+                          ->GetScriptContext()
+                          ->GetOrCreateJSBindingFromMap(pParent)
+                          .As<v8::Value>()
+                    : fxv8::NewNullHelper(pIsolate).As<v8::Value>();
 }
 
-void CJX_Tree::index(CFXJSE_Value* pValue,
+void CJX_Tree::index(v8::Isolate* pIsolate,
+                     v8::Local<v8::Value>* pValue,
                      bool bSetting,
                      XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  CXFA_Node* pNode = ToNode(GetXFAObject());
+  CXFA_Node* pNode = GetXFANode();
   size_t iIndex = pNode ? pNode->GetIndexByName() : 0;
-  pValue->SetInteger(pdfium::base::checked_cast<int32_t>(iIndex));
+  *pValue = fxv8::NewNumberHelper(pIsolate,
+                                  pdfium::base::checked_cast<int32_t>(iIndex));
 }
 
-void CJX_Tree::classIndex(CFXJSE_Value* pValue,
+void CJX_Tree::classIndex(v8::Isolate* pIsolate,
+                          v8::Local<v8::Value>* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  CXFA_Node* pNode = ToNode(GetXFAObject());
+  CXFA_Node* pNode = GetXFANode();
   size_t iIndex = pNode ? pNode->GetIndexByClassName() : 0;
-  pValue->SetInteger(pdfium::base::checked_cast<int32_t>(iIndex));
+  *pValue = fxv8::NewNumberHelper(pIsolate,
+                                  pdfium::base::checked_cast<int32_t>(iIndex));
 }
 
-void CJX_Tree::somExpression(CFXJSE_Value* pValue,
+void CJX_Tree::somExpression(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value>* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
   if (bSetting) {
-    ThrowInvalidPropertyException();
+    ThrowInvalidPropertyException(pIsolate);
     return;
   }
 
-  WideString wsSOMExpression = GetXFAObject()->GetSOMExpression();
-  pValue->SetString(wsSOMExpression.ToUTF8().AsStringView());
+  ByteString bsSOMExpression = GetXFAObject()->GetSOMExpression().ToUTF8();
+  *pValue = fxv8::NewStringHelper(pIsolate, bsSOMExpression.AsStringView());
 }
 
-void CJX_Tree::ResolveNodeList(CFXJSE_Value* pValue,
-                               WideString wsExpression,
-                               uint32_t dwFlag,
-                               CXFA_Node* refNode) {
+v8::Local<v8::Value> CJX_Tree::ResolveNodeList(v8::Isolate* pIsolate,
+                                               WideString wsExpression,
+                                               Mask<XFA_ResolveFlag> dwFlag,
+                                               CXFA_Node* refNode) {
   if (!refNode)
-    refNode = ToNode(GetXFAObject());
+    refNode = GetXFANode();
 
-  XFA_RESOLVENODE_RS resolveNodeRS;
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(),
-                                 &resolveNodeRS, dwFlag, nullptr);
-  CXFA_ArrayNodeList* pNodeList = new CXFA_ArrayNodeList(GetDocument());
-  if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    for (auto& pObject : resolveNodeRS.objects) {
-      if (pObject->IsNode())
-        pNodeList->Append(pObject->AsNode());
-    }
-  } else {
-    if (resolveNodeRS.script_attribute.callback &&
-        resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) {
-      for (auto& pObject : resolveNodeRS.objects) {
-        auto innerValue =
-            pdfium::MakeUnique<CFXJSE_Value>(pScriptContext->GetIsolate());
-        CJX_Object* jsObject = pObject->JSObject();
-        (*resolveNodeRS.script_attribute.callback)(
-            jsObject, innerValue.get(), false,
-            resolveNodeRS.script_attribute.attribute);
-        CXFA_Object* obj = CFXJSE_Engine::ToObject(innerValue.get());
-        if (obj->IsNode())
-          pNodeList->Append(obj->AsNode());
+  CXFA_Document* pDoc = GetDocument();
+  auto* pNodeList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
+      pDoc->GetHeap()->GetAllocationHandle(), pDoc);
+  pDoc->GetNodeOwner()->PersistList(pNodeList);
+
+  CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
+  absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
+      pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(),
+                                     dwFlag);
+
+  if (maybeResult.has_value()) {
+    if (maybeResult.value().type ==
+        CFXJSE_Engine::ResolveResult::Type::kNodes) {
+      for (auto& pObject : maybeResult.value().objects) {
+        if (pObject->IsNode())
+          pNodeList->Append(pObject->AsNode());
+      }
+    } else {
+      if (maybeResult.value().script_attribute.callback &&
+          maybeResult.value().script_attribute.eValueType ==
+              XFA_ScriptType::Object) {
+        for (auto& pObject : maybeResult.value().objects) {
+          v8::Local<v8::Value> innerValue;
+          CJX_Object* jsObject = pObject->JSObject();
+          (*maybeResult.value().script_attribute.callback)(
+              pIsolate, jsObject, &innerValue, false,
+              maybeResult.value().script_attribute.attribute);
+          CXFA_Object* obj =
+              CFXJSE_Engine::ToObject(pScriptContext->GetIsolate(), innerValue);
+          if (obj->IsNode())
+            pNodeList->Append(obj->AsNode());
+        }
       }
     }
   }
-  pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass());
+  return pNodeList->JSObject()->NewBoundV8Object(
+      pIsolate, pScriptContext->GetJseNormalClass()->GetTemplate(pIsolate));
 }
diff --git a/fxjs/xfa/cjx_tree.h b/fxjs/xfa/cjx_tree.h
index cbef888..42c522e 100644
--- a/fxjs/xfa/cjx_tree.h
+++ b/fxjs/xfa/cjx_tree.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #ifndef FXJS_XFA_CJX_TREE_H_
 #define FXJS_XFA_CJX_TREE_H_
 
+#include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cjx_object.h"
 #include "fxjs/xfa/jse_define.h"
 
@@ -15,7 +16,7 @@
 
 class CJX_Tree : public CJX_Object {
  public:
-  explicit CJX_Tree(CXFA_Object* obj);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Tree() override;
 
   // CJX_Object:
@@ -32,6 +33,9 @@
   JSE_PROP(parent);
   JSE_PROP(somExpression);
 
+ protected:
+  explicit CJX_Tree(CXFA_Object* obj);
+
  private:
   using Type__ = CJX_Tree;
   using ParentType__ = CJX_Object;
@@ -39,10 +43,10 @@
   static const TypeTag static_type__ = TypeTag::Tree;
   static const CJX_MethodSpec MethodSpecs[];
 
-  void ResolveNodeList(CFXJSE_Value* pValue,
-                       WideString wsExpression,
-                       uint32_t dwFlag,
-                       CXFA_Node* refNode);
+  v8::Local<v8::Value> ResolveNodeList(v8::Isolate* pIsolate,
+                                       WideString wsExpression,
+                                       Mask<XFA_ResolveFlag> dwFlag,
+                                       CXFA_Node* refNode);
 };
 
 #endif  // FXJS_XFA_CJX_TREE_H_
diff --git a/fxjs/xfa/cjx_treelist.cpp b/fxjs/xfa/cjx_treelist.cpp
index 51bbb0a..e2e4a21 100644
--- a/fxjs/xfa/cjx_treelist.cpp
+++ b/fxjs/xfa/cjx_treelist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_node.h"
 #include "xfa/fxfa/parser/cxfa_treelist.h"
@@ -22,7 +23,7 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_TreeList::~CJX_TreeList() {}
+CJX_TreeList::~CJX_TreeList() = default;
 
 bool CJX_TreeList::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
@@ -33,7 +34,7 @@
 }
 
 CJS_Result CJX_TreeList::namedItem(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -43,9 +44,6 @@
   if (!pNode)
     return CJS_Result::Success();
 
-  CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
-
   return CJS_Result::Success(
-      value->DirectGetValue().Get(runtime->GetIsolate()));
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode));
 }
diff --git a/fxjs/xfa/cjx_treelist.h b/fxjs/xfa/cjx_treelist.h
index 294ab26..a5dbaaa 100644
--- a/fxjs/xfa/cjx_treelist.h
+++ b/fxjs/xfa/cjx_treelist.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_TreeList final : public CJX_List {
  public:
-  explicit CJX_TreeList(CXFA_TreeList* list);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_TreeList() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_METHOD(namedItem);
 
  private:
+  explicit CJX_TreeList(CXFA_TreeList* list);
+
   using Type__ = CJX_TreeList;
   using ParentType__ = CJX_List;
 
diff --git a/fxjs/xfa/cjx_wsdlconnection.cpp b/fxjs/xfa/cjx_wsdlconnection.cpp
index 7821232..1f7f45e 100644
--- a/fxjs/xfa/cjx_wsdlconnection.cpp
+++ b/fxjs/xfa/cjx_wsdlconnection.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,7 @@
 #include "fxjs/cfx_v8.h"
 #include "fxjs/js_resources.h"
 #include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-primitive.h"
 #include "xfa/fxfa/parser/cxfa_wsdlconnection.h"
 
 const CJX_MethodSpec CJX_WsdlConnection::MethodSpecs[] = {
@@ -21,14 +22,14 @@
   DefineMethods(MethodSpecs);
 }
 
-CJX_WsdlConnection::~CJX_WsdlConnection() {}
+CJX_WsdlConnection::~CJX_WsdlConnection() = default;
 
 bool CJX_WsdlConnection::DynamicTypeIs(TypeTag eType) const {
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
 CJS_Result CJX_WsdlConnection::execute(
-    CFX_V8* runtime,
+    CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty() && params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_wsdlconnection.h b/fxjs/xfa/cjx_wsdlconnection.h
index bd5db40..8706695 100644
--- a/fxjs/xfa/cjx_wsdlconnection.h
+++ b/fxjs/xfa/cjx_wsdlconnection.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_WsdlConnection final : public CJX_Node {
  public:
-  explicit CJX_WsdlConnection(CXFA_WsdlConnection* connection);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_WsdlConnection() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_METHOD(execute);
 
  private:
+  explicit CJX_WsdlConnection(CXFA_WsdlConnection* connection);
+
   using Type__ = CJX_WsdlConnection;
   using ParentType__ = CJX_Node;
 
diff --git a/fxjs/xfa/cjx_xfa.cpp b/fxjs/xfa/cjx_xfa.cpp
index 08c6a80..5248693 100644
--- a/fxjs/xfa/cjx_xfa.cpp
+++ b/fxjs/xfa/cjx_xfa.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,9 @@
 
 #include "fxjs/xfa/cjx_xfa.h"
 
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
-#include "fxjs/xfa/cfxjse_value.h"
+#include "v8/include/v8-object.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_xfa.h"
 
@@ -19,7 +20,8 @@
   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
 }
 
-void CJX_Xfa::thisValue(CFXJSE_Value* pValue,
+void CJX_Xfa::thisValue(v8::Isolate* pIsolate,
+                        v8::Local<v8::Value>* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
   if (bSetting)
@@ -27,9 +29,7 @@
 
   auto* pScriptContext = GetDocument()->GetScriptContext();
   CXFA_Object* pThis = pScriptContext->GetThisObject();
-  if (!pThis) {
-    pValue->SetNull();
-    return;
-  }
-  pValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(pThis));
+  *pValue =
+      pThis ? pScriptContext->GetOrCreateJSBindingFromMap(pThis).As<v8::Value>()
+            : fxv8::NewNullHelper(pIsolate);
 }
diff --git a/fxjs/xfa/cjx_xfa.h b/fxjs/xfa/cjx_xfa.h
index 63b0f5f..031425d 100644
--- a/fxjs/xfa/cjx_xfa.h
+++ b/fxjs/xfa/cjx_xfa.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,7 +14,7 @@
 
 class CJX_Xfa final : public CJX_Model {
  public:
-  explicit CJX_Xfa(CXFA_Xfa* node);
+  CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
   ~CJX_Xfa() override;
 
   // CJX_Object:
@@ -23,6 +23,8 @@
   JSE_PROP(thisValue); /* this */
 
  private:
+  explicit CJX_Xfa(CXFA_Xfa* node);
+
   using Type__ = CJX_Xfa;
   using ParentType__ = CJX_Model;
 
diff --git a/fxjs/xfa/fxjse.cpp b/fxjs/xfa/fxjse.cpp
index ff6176b..d03ca7e 100644
--- a/fxjs/xfa/fxjse.cpp
+++ b/fxjs/xfa/fxjse.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,12 @@
 
 #include "fxjs/xfa/fxjse.h"
 
+#include "fxjs/fxv8.h"
+#include "fxjs/xfa/cfxjse_context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-object.h"
+#include "v8/include/v8-template.h"
+
 namespace pdfium {
 namespace fxjse {
 
@@ -15,6 +21,14 @@
 }  // namespace fxjse
 }  // namespace pdfium
 
+// static
+CFXJSE_HostObject* CFXJSE_HostObject::FromV8(v8::Local<v8::Value> arg) {
+  if (!fxv8::IsObject(arg))
+    return nullptr;
+
+  return FXJSE_RetrieveObjectBinding(arg.As<v8::Object>());
+}
+
 CFXJSE_HostObject::CFXJSE_HostObject() = default;
 
 CFXJSE_HostObject::~CFXJSE_HostObject() = default;
@@ -23,6 +37,17 @@
   return nullptr;
 }
 
-CXFA_Object* CFXJSE_HostObject::AsCXFAObject() {
+CJX_Object* CFXJSE_HostObject::AsCJXObject() {
   return nullptr;
 }
+
+v8::Local<v8::Object> CFXJSE_HostObject::NewBoundV8Object(
+    v8::Isolate* pIsolate,
+    v8::Local<v8::FunctionTemplate> tmpl) {
+  v8::Local<v8::Object> hObject =
+      tmpl->InstanceTemplate()
+          ->NewInstance(pIsolate->GetCurrentContext())
+          .ToLocalChecked();
+  FXJSE_UpdateObjectBinding(hObject, this);
+  return hObject;
+}
diff --git a/fxjs/xfa/fxjse.h b/fxjs/xfa/fxjse.h
index 5aa6b39..b020ce6 100644
--- a/fxjs/xfa/fxjse.h
+++ b/fxjs/xfa/fxjse.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,79 +7,90 @@
 #ifndef FXJS_XFA_FXJSE_H_
 #define FXJS_XFA_FXJSE_H_
 
+#include <stdint.h>
+
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-forward.h"
 
 namespace pdfium {
 namespace fxjse {
 
+// These are strings by design. With ASLR, their addresses should be random, so
+// it should be very unlikely for an object to accidentally have the same tag.
 extern const char kFuncTag[];
 extern const char kClassTag[];
 
 }  // namespace fxjse
 }  // namespace pdfium
 
-class CFXJSE_Arguments;
 class CFXJSE_FormCalcContext;
-class CFXJSE_Value;
 class CJS_Result;
-class CXFA_Object;
+class CJX_Object;
+
+enum class FXJSE_ClassPropType {
+  kNone,
+  kProperty,
+  kMethod,
+};
 
 // C++ object which is retrieved from v8 object's slot.
 class CFXJSE_HostObject {
  public:
+  static CFXJSE_HostObject* FromV8(v8::Local<v8::Value> arg);
   virtual ~CFXJSE_HostObject();
 
   // Two subclasses.
   virtual CFXJSE_FormCalcContext* AsFormCalcContext();
-  virtual CXFA_Object* AsCXFAObject();
+  virtual CJX_Object* AsCJXObject();
+
+  v8::Local<v8::Object> NewBoundV8Object(v8::Isolate* pIsolate,
+                                         v8::Local<v8::FunctionTemplate> tmpl);
 
  protected:
   CFXJSE_HostObject();
 };
 
-typedef CJS_Result (*FXJSE_MethodCallback)(
-    const v8::FunctionCallbackInfo<v8::Value>& info,
-    const WideString& functionName);
-typedef void (*FXJSE_FuncCallback)(CFXJSE_Value* pThis,
-                                   ByteStringView szFuncName,
-                                   CFXJSE_Arguments& args);
-typedef void (*FXJSE_PropAccessor)(CFXJSE_Value* pObject,
-                                   ByteStringView szPropName,
-                                   CFXJSE_Value* pValue);
-typedef int32_t (*FXJSE_PropTypeGetter)(CFXJSE_Value* pObject,
-                                        ByteStringView szPropName,
-                                        bool bQueryIn);
-
-enum FXJSE_ClassPropTypes {
-  FXJSE_ClassPropType_None,
-  FXJSE_ClassPropType_Property,
-  FXJSE_ClassPropType_Method
-};
+using FXJSE_MethodCallback =
+    CJS_Result (*)(const v8::FunctionCallbackInfo<v8::Value>& info,
+                   const WideString& functionName);
+using FXJSE_FuncCallback =
+    void (*)(CFXJSE_HostObject* pThis,
+             const v8::FunctionCallbackInfo<v8::Value>& info);
+using FXJSE_PropGetter = v8::Local<v8::Value> (*)(v8::Isolate* pIsolate,
+                                                  v8::Local<v8::Object> pObject,
+                                                  ByteStringView szPropName);
+using FXJSE_PropSetter = void (*)(v8::Isolate* pIsolate,
+                                  v8::Local<v8::Object> pObject,
+                                  ByteStringView szPropName,
+                                  v8::Local<v8::Value> pValue);
+using FXJSE_PropTypeGetter =
+    FXJSE_ClassPropType (*)(v8::Isolate* pIsolate,
+                            v8::Local<v8::Object> pObject,
+                            ByteStringView szPropName,
+                            bool bQueryIn);
 
 struct FXJSE_FUNCTION_DESCRIPTOR {
-  const char* tag;  // pdfium::kFuncTag always.
+  const char* tag;  // `pdfium::fxjse::kFuncTag` always.
   const char* name;
   FXJSE_FuncCallback callbackProc;
 };
 
 struct FXJSE_CLASS_DESCRIPTOR {
-  const char* tag;  // pdfium::kClassTag always.
+  const char* tag;  // `pdfium::fxjse::kClassTag` always.
   const char* name;
   const FXJSE_FUNCTION_DESCRIPTOR* methods;
   int32_t methNum;
   FXJSE_PropTypeGetter dynPropTypeGetter;
-  FXJSE_PropAccessor dynPropGetter;
-  FXJSE_PropAccessor dynPropSetter;
+  FXJSE_PropGetter dynPropGetter;
+  FXJSE_PropSetter dynPropSetter;
   FXJSE_MethodCallback dynMethodCall;
 };
 
-extern const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor;
-extern const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor;
-extern const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor;
-extern const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kGlobalClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kNormalClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kVariablesClassDescriptor;
+extern const FXJSE_CLASS_DESCRIPTOR kFormCalcDescriptor;
 
-void FXJSE_ThrowMessage(ByteStringView utf8Message);
+void FXJSE_ThrowMessage(v8::Isolate* pIsolate, ByteStringView utf8Message);
 
 #endif  // FXJS_XFA_FXJSE_H_
diff --git a/fxjs/xfa/jse_define.h b/fxjs/xfa/jse_define.h
index 26405c9..0800601 100644
--- a/fxjs/xfa/jse_define.h
+++ b/fxjs/xfa/jse_define.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,25 +11,28 @@
 
 #include "fxjs/cjs_result.h"
 
-class CFX_V8;
+class CFXJSE_Engine;
 
 #define JSE_METHOD(method_name)                                      \
   static CJS_Result method_name##_static(                            \
-      CJX_Object* node, CFX_V8* runtime,                             \
+      CJX_Object* node, CFXJSE_Engine* runtime,                      \
       const std::vector<v8::Local<v8::Value>>& params) {             \
     if (!node->DynamicTypeIs(static_type__))                         \
       return CJS_Result::Failure(JSMessage::kBadObjectError);        \
     return static_cast<Type__*>(node)->method_name(runtime, params); \
   }                                                                  \
-  CJS_Result method_name(CFX_V8* runtime,                            \
+  CJS_Result method_name(CFXJSE_Engine* runtime,                     \
                          const std::vector<v8::Local<v8::Value>>& params)
 
-#define JSE_PROP(prop_name)                                               \
-  static void prop_name##_static(CJX_Object* node, CFXJSE_Value* value,   \
-                                 bool setting, XFA_Attribute attribute) { \
-    if (node->DynamicTypeIs(static_type__))                               \
-      static_cast<Type__*>(node)->prop_name(value, setting, attribute);   \
-  }                                                                       \
-  void prop_name(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute)
+#define JSE_PROP(prop_name)                                                 \
+  static void prop_name##_static(v8::Isolate* pIsolate, CJX_Object* node,   \
+                                 v8::Local<v8::Value>* value, bool setting, \
+                                 XFA_Attribute attribute) {                 \
+    if (node->DynamicTypeIs(static_type__))                                 \
+      static_cast<Type__*>(node)->prop_name(pIsolate, value, setting,       \
+                                            attribute);                     \
+  }                                                                         \
+  void prop_name(v8::Isolate* pIsolate, v8::Local<v8::Value>* pValue,       \
+                 bool bSetting, XFA_Attribute eAttribute)
 
 #endif  // FXJS_XFA_JSE_DEFINE_H_