Move CFXA_MapModule to CFXJSE_MapModule and add tests

Change-Id: Ib1bf4c0394d435658e11a2237442b2e13c46280f
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/74832
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/fxjs/BUILD.gn b/fxjs/BUILD.gn
index 0526f21..1c7c3e3 100644
--- a/fxjs/BUILD.gn
+++ b/fxjs/BUILD.gn
@@ -124,6 +124,8 @@
         "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",
@@ -253,8 +255,12 @@
         "gc/gced_tree_node_unittest.cpp",
         "gc/heap_unittest.cpp",
         "gc/move_unittest.cpp",
+        "xfa/cfxjse_mapmodule_unittest.cpp",
       ]
-      deps += [ ":gc" ]
+      deps += [
+        ":gc",
+        "../xfa/fxfa/parser",
+      ]
     }
   }
 
diff --git a/fxjs/xfa/cfxjse_mapmodule.cpp b/fxjs/xfa/cfxjse_mapmodule.cpp
new file mode 100644
index 0000000..669ce21
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule.cpp
@@ -0,0 +1,78 @@
+// Copyright 2020 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_mapmodule.h"
+
+#include "third_party/base/stl_util.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;
+}
+
+Optional<int32_t> CFXJSE_MapModule::GetValue(uint32_t key) const {
+  auto it = m_ValueMap.find(key);
+  if (it == m_ValueMap.end())
+    return pdfium::nullopt;
+  return it->second;
+}
+
+Optional<WideString> CFXJSE_MapModule::GetString(uint32_t key) const {
+  auto it = m_StringMap.find(key);
+  if (it == m_StringMap.end())
+    return pdfium::nullopt;
+  return it->second;
+}
+
+Optional<CXFA_Measurement> CFXJSE_MapModule::GetMeasurement(
+    uint32_t key) const {
+  auto it = m_MeasurementMap.find(key);
+  if (it == m_MeasurementMap.end())
+    return pdfium::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..ce8fa07
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule.h
@@ -0,0 +1,44 @@
+// Copyright 2020 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_MAPMODULE_H_
+#define FXJS_XFA_CFXJSE_MAPMODULE_H_
+
+#include <stdint.h>
+
+#include <map>
+
+#include "core/fxcrt/fx_string.h"
+#include "third_party/base/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);
+  Optional<int32_t> GetValue(uint32_t key) const;
+  Optional<WideString> GetString(uint32_t key) const;
+  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..894b209
--- /dev/null
+++ b/fxjs/xfa/cfxjse_mapmodule_unittest.cpp
@@ -0,0 +1,124 @@
+// Copyright 2020 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_mapmodule.h"
+
+#include "core/fxcrt/fx_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/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 string(L"foo");
+  CXFA_Measurement measure(L"1 pt");
+  CFXJSE_MapModule module;
+
+  module.SetValue(100, value);
+  module.SetString(200, string);
+  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(), string);
+  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 string(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, string);
+  EXPECT_TRUE(module.HasKey(100));
+  EXPECT_FALSE(module.GetValue(100).has_value());
+  EXPECT_EQ(module.GetString(100).value(), string);
+  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/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
index db7deeb..eb975b1 100644
--- a/fxjs/xfa/cjx_object.cpp
+++ b/fxjs/xfa/cjx_object.cpp
@@ -16,6 +16,7 @@
 #include "fxjs/cjs_result.h"
 #include "fxjs/gc/container_trace.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_mapmodule.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_boolean.h"
 #include "fxjs/xfa/cjx_draw.h"
@@ -93,80 +94,6 @@
 
 }  // namespace
 
-class CXFA_MapModule {
- public:
-  CXFA_MapModule() = default;
-  ~CXFA_MapModule() = default;
-
-  void SetValue(uint32_t key, int32_t value) {
-    m_StringMap.erase(key);
-    m_MeasurementMap.erase(key);
-    m_ValueMap[key] = value;
-  }
-
-  void SetString(uint32_t key, const WideString& wsString) {
-    m_ValueMap.erase(key);
-    m_MeasurementMap.erase(key);
-    m_StringMap[key] = wsString;
-  }
-
-  void SetMeasurement(uint32_t key, const CXFA_Measurement& measurement) {
-    m_ValueMap.erase(key);
-    m_StringMap.erase(key);
-    m_MeasurementMap[key] = measurement;
-  }
-
-  Optional<int32_t> GetValue(uint32_t key) const {
-    auto it = m_ValueMap.find(key);
-    if (it == m_ValueMap.end())
-      return pdfium::nullopt;
-    return it->second;
-  }
-
-  Optional<WideString> GetString(uint32_t key) const {
-    auto it = m_StringMap.find(key);
-    if (it == m_StringMap.end())
-      return pdfium::nullopt;
-    return it->second;
-  }
-
-  Optional<CXFA_Measurement> GetMeasurement(uint32_t key) const {
-    auto it = m_MeasurementMap.find(key);
-    if (it == m_MeasurementMap.end())
-      return pdfium::nullopt;
-    return it->second;
-  }
-
-  bool HasKey(uint32_t key) const {
-    return pdfium::Contains(m_ValueMap, key) ||
-           pdfium::Contains(m_StringMap, key) ||
-           pdfium::Contains(m_MeasurementMap, key);
-  }
-
-  void RemoveKey(uint32_t key) {
-    m_ValueMap.erase(key);
-    m_StringMap.erase(key);
-    m_MeasurementMap.erase(key);
-  }
-
-  void MergeDataFrom(const CXFA_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);
-  }
-
- 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;
-};
-
 CJX_Object::CJX_Object(CXFA_Object* obj) : object_(obj) {}
 
 CJX_Object::~CJX_Object() = default;
@@ -832,13 +759,13 @@
   return GetXFANode()->GetOrCreateProperty(index, eProperty);
 }
 
-CXFA_MapModule* CJX_Object::CreateMapModule() {
+CFXJSE_MapModule* CJX_Object::CreateMapModule() {
   if (!map_module_)
-    map_module_ = std::make_unique<CXFA_MapModule>();
+    map_module_ = std::make_unique<CFXJSE_MapModule>();
   return map_module_.get();
 }
 
-CXFA_MapModule* CJX_Object::GetMapModule() const {
+CFXJSE_MapModule* CJX_Object::GetMapModule() const {
   return map_module_.get();
 }
 
@@ -856,14 +783,14 @@
 }
 
 Optional<int32_t> CJX_Object::GetMapModuleValue(uint32_t key) const {
-  CXFA_MapModule* pModule = GetMapModule();
+  CFXJSE_MapModule* pModule = GetMapModule();
   if (!pModule)
     return pdfium::nullopt;
   return pModule->GetValue(key);
 }
 
 Optional<WideString> CJX_Object::GetMapModuleString(uint32_t key) const {
-  CXFA_MapModule* pModule = GetMapModule();
+  CFXJSE_MapModule* pModule = GetMapModule();
   if (!pModule)
     return pdfium::nullopt;
   return pModule->GetString(key);
@@ -871,7 +798,7 @@
 
 Optional<CXFA_Measurement> CJX_Object::GetMapModuleMeasurement(
     uint32_t key) const {
-  CXFA_MapModule* pModule = GetMapModule();
+  CFXJSE_MapModule* pModule = GetMapModule();
   if (!pModule)
     return pdfium::nullopt;
   return pModule->GetMeasurement(key);
@@ -933,19 +860,19 @@
 }
 
 bool CJX_Object::HasMapModuleKey(uint32_t key) {
-  CXFA_MapModule* pModule = GetMapModule();
+  CFXJSE_MapModule* pModule = GetMapModule();
   return pModule && pModule->HasKey(key);
 }
 
 void CJX_Object::RemoveMapModuleKey(uint32_t key) {
-  CXFA_MapModule* pModule = GetMapModule();
+  CFXJSE_MapModule* pModule = GetMapModule();
   if (pModule)
     pModule->RemoveKey(key);
 }
 
 void CJX_Object::MergeAllData(CXFA_Object* pDstObj) {
-  CXFA_MapModule* pDstModule = ToNode(pDstObj)->JSObject()->CreateMapModule();
-  CXFA_MapModule* pSrcModule = GetMapModule();
+  CFXJSE_MapModule* pDstModule = ToNode(pDstObj)->JSObject()->CreateMapModule();
+  CFXJSE_MapModule* pSrcModule = GetMapModule();
   if (!pSrcModule)
     return;
 
diff --git a/fxjs/xfa/cjx_object.h b/fxjs/xfa/cjx_object.h
index 9a01b76..8cd5ab0 100644
--- a/fxjs/xfa/cjx_object.h
+++ b/fxjs/xfa/cjx_object.h
@@ -21,15 +21,15 @@
 #include "v8/include/cppgc/garbage-collected.h"
 #include "v8/include/cppgc/member.h"
 #include "xfa/fxfa/fxfa_basic.h"
-#include "xfa/fxfa/parser/cxfa_measurement.h"
 
+class CFXJSE_MapModule;
 class CFXJSE_Value;
 class CFX_V8;
 class CFX_XMLElement;
 class CJX_Object;
 class CXFA_Document;
 class CXFA_LayoutItem;
-class CXFA_MapModule;
+class CXFA_Measurement;
 class CXFA_Node;
 class CXFA_Object;
 
@@ -255,8 +255,8 @@
   CFX_XMLElement* SetValue(XFA_Attribute eAttr, int32_t value, bool bNotify);
   int32_t Subform_and_SubformSet_InstanceIndex();
 
-  CXFA_MapModule* CreateMapModule();
-  CXFA_MapModule* GetMapModule() const;
+  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);
@@ -274,7 +274,7 @@
   cppgc::Member<CXFA_Object> object_;
   cppgc::Member<CXFA_LayoutItem> layout_item_;
   cppgc::Member<CalcData> calc_data_;
-  std::unique_ptr<CXFA_MapModule> map_module_;
+  std::unique_ptr<CFXJSE_MapModule> map_module_;
   std::map<ByteString, CJX_MethodCall> method_specs_;
   size_t calc_recursion_count_ = 0;
 };