[xfa] Move CJX code into fxjs/xfa

This CL moves the remaining CJX clases into fxjs/xfa so they co-exist
with the rest of the CJX classes.

Change-Id: I2b1e5504ac23f73df177f309967a04ab27fb61d1
Reviewed-on: https://pdfium-review.googlesource.com/20550
Commit-Queue: dsinclair <dsinclair@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/fxjs/xfa/cjx_container.h b/fxjs/xfa/cjx_container.h
index 41d11cf..0e2170f 100644
--- a/fxjs/xfa/cjx_container.h
+++ b/fxjs/xfa/cjx_container.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_CONTAINER_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_Node;
 
diff --git a/fxjs/xfa/cjx_content.h b/fxjs/xfa/cjx_content.h
index cd2e71a..983a750 100644
--- a/fxjs/xfa/cjx_content.h
+++ b/fxjs/xfa/cjx_content.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_CONTENT_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_object.h"
+#include "fxjs/xfa/cjx_object.h"
 
 class CXFA_Content;
 
diff --git a/fxjs/xfa/cjx_datawindow.cpp b/fxjs/xfa/cjx_datawindow.cpp
new file mode 100644
index 0000000..54f3204
--- /dev/null
+++ b/fxjs/xfa/cjx_datawindow.cpp
@@ -0,0 +1,66 @@
+// Copyright 2017 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/cjx_datawindow.h"
+
+#include <vector>
+
+#include "fxjs/cfxjse_value.h"
+#include "xfa/fxfa/parser/cscript_datawindow.h"
+
+const CJX_MethodSpec CJX_DataWindow::MethodSpecs[] = {
+    {"gotoRecord", gotoRecord_static},
+    {"isRecordGroup", isRecordGroup_static},
+    {"moveCurrentRecord", moveCurrentRecord_static},
+    {"record", record_static},
+    {"", nullptr}};
+
+CJX_DataWindow::CJX_DataWindow(CScript_DataWindow* window)
+    : CJX_Object(window) {
+  DefineMethods(MethodSpecs);
+}
+
+CJX_DataWindow::~CJX_DataWindow() {}
+
+CJS_Return CJX_DataWindow::moveCurrentRecord(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_DataWindow::record(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_DataWindow::gotoRecord(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_DataWindow::isRecordGroup(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+void CJX_DataWindow::RecordsBefore(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {}
+
+void CJX_DataWindow::CurrentRecordNumber(CFXJSE_Value* pValue,
+                                         bool bSetting,
+                                         XFA_Attribute eAttribute) {}
+
+void CJX_DataWindow::RecordsAfter(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {}
+
+void CJX_DataWindow::IsDefined(CFXJSE_Value* pValue,
+                               bool bSetting,
+                               XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_datawindow.h b/fxjs/xfa/cjx_datawindow.h
new file mode 100644
index 0000000..82c5b1a
--- /dev/null
+++ b/fxjs/xfa/cjx_datawindow.h
@@ -0,0 +1,42 @@
+// Copyright 2017 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_CJX_DATAWINDOW_H_
+#define FXJS_XFA_CJX_DATAWINDOW_H_
+
+#include "fxjs/CJX_Define.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+class CFXJSE_Value;
+class CScript_DataWindow;
+
+class CJX_DataWindow : public CJX_Object {
+ public:
+  explicit CJX_DataWindow(CScript_DataWindow* window);
+  ~CJX_DataWindow() override;
+
+  void RecordsBefore(CFXJSE_Value* pValue,
+                     bool bSetting,
+                     XFA_Attribute eAttribute);
+  void CurrentRecordNumber(CFXJSE_Value* pValue,
+                           bool bSetting,
+                           XFA_Attribute eAttribute);
+  void RecordsAfter(CFXJSE_Value* pValue,
+                    bool bSetting,
+                    XFA_Attribute eAttribute);
+  void IsDefined(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+
+  JS_METHOD(gotoRecord, CJX_DataWindow);
+  JS_METHOD(isRecordGroup, CJX_DataWindow);
+  JS_METHOD(moveCurrentRecord, CJX_DataWindow);
+  JS_METHOD(record, CJX_DataWindow);
+
+ private:
+  static const CJX_MethodSpec MethodSpecs[];
+};
+
+#endif  // FXJS_XFA_CJX_DATAWINDOW_H_
diff --git a/fxjs/xfa/cjx_delta.h b/fxjs/xfa/cjx_delta.h
index d9a2c62..29d3e8e 100644
--- a/fxjs/xfa/cjx_delta.h
+++ b/fxjs/xfa/cjx_delta.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_DELTA_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_object.h"
+#include "fxjs/xfa/cjx_object.h"
 
 class CXFA_Delta;
 
diff --git a/fxjs/xfa/cjx_desc.h b/fxjs/xfa/cjx_desc.h
index 602633d..f3c9f46 100644
--- a/fxjs/xfa/cjx_desc.h
+++ b/fxjs/xfa/cjx_desc.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_DESC_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_Desc;
 
diff --git a/fxjs/xfa/cjx_eventpseudomodel.cpp b/fxjs/xfa/cjx_eventpseudomodel.cpp
new file mode 100644
index 0000000..a109d1c
--- /dev/null
+++ b/fxjs/xfa/cjx_eventpseudomodel.cpp
@@ -0,0 +1,257 @@
+// Copyright 2017 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/cjx_eventpseudomodel.h"
+
+#include <vector>
+
+#include "fxjs/cfxjse_engine.h"
+#include "fxjs/cfxjse_value.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* pValue, WideString& wsValue, bool bSetting) {
+  if (bSetting) {
+    wsValue = pValue->ToWideString();
+    return;
+  }
+  pValue->SetString(wsValue.UTF8Encode().AsStringView());
+}
+
+void InterProperty(CFXJSE_Value* pValue, int32_t& iValue, bool bSetting) {
+  if (bSetting) {
+    iValue = pValue->ToInteger();
+    return;
+  }
+  pValue->SetInteger(iValue);
+}
+
+void BooleanProperty(CFXJSE_Value* pValue, bool& bValue, bool bSetting) {
+  if (bSetting) {
+    bValue = pValue->ToBoolean();
+    return;
+  }
+  pValue->SetBoolean(bValue);
+}
+
+}  // namespace
+
+const CJX_MethodSpec CJX_EventPseudoModel::MethodSpecs[] = {
+    {"emit", emit_static},
+    {"reset", reset_static},
+    {"", nullptr}};
+
+CJX_EventPseudoModel::CJX_EventPseudoModel(CScript_EventPseudoModel* model)
+    : CJX_Object(model) {
+  DefineMethods(MethodSpecs);
+}
+
+CJX_EventPseudoModel::~CJX_EventPseudoModel() {}
+
+void CJX_EventPseudoModel::Change(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::Change, bSetting);
+}
+
+void CJX_EventPseudoModel::CommitKey(CFXJSE_Value* pValue,
+                                     bool bSetting,
+                                     XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::CommitKey, bSetting);
+}
+
+void CJX_EventPseudoModel::FullText(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::FullText, bSetting);
+}
+
+void CJX_EventPseudoModel::KeyDown(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::Keydown, bSetting);
+}
+
+void CJX_EventPseudoModel::Modifier(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::Modifier, bSetting);
+}
+
+void CJX_EventPseudoModel::NewContentType(CFXJSE_Value* pValue,
+                                          bool bSetting,
+                                          XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::NewContentType, bSetting);
+}
+
+void CJX_EventPseudoModel::NewText(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::NewText, bSetting);
+}
+
+void CJX_EventPseudoModel::PrevContentType(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::PreviousContentType, bSetting);
+}
+
+void CJX_EventPseudoModel::PrevText(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::PreviousText, bSetting);
+}
+
+void CJX_EventPseudoModel::Reenter(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::Reenter, bSetting);
+}
+
+void CJX_EventPseudoModel::SelEnd(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::SelectionEnd, bSetting);
+}
+
+void CJX_EventPseudoModel::SelStart(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::SelectionStart, bSetting);
+}
+
+void CJX_EventPseudoModel::Shift(CFXJSE_Value* pValue,
+                                 bool bSetting,
+                                 XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::Shift, bSetting);
+}
+
+void CJX_EventPseudoModel::SoapFaultCode(CFXJSE_Value* pValue,
+                                         bool bSetting,
+                                         XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::SoapFaultCode, bSetting);
+}
+
+void CJX_EventPseudoModel::SoapFaultString(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::SoapFaultString, bSetting);
+}
+
+void CJX_EventPseudoModel::Target(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {
+  Property(pValue, XFA_Event::Target, bSetting);
+}
+
+CJS_Return CJX_EventPseudoModel::emit(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
+  if (!pScriptContext)
+    return CJS_Return(true);
+
+  CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
+  if (!pEventParam)
+    return CJS_Return(true);
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  CXFA_FFWidgetHandler* pWidgetHandler = pNotify->GetWidgetHandler();
+  if (!pWidgetHandler)
+    return CJS_Return(true);
+
+  pWidgetHandler->ProcessEvent(pEventParam->m_pTarget, pEventParam);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_EventPseudoModel::reset(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
+  if (!pScriptContext)
+    return CJS_Return(true);
+
+  CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
+  if (!pEventParam)
+    return CJS_Return(true);
+
+  pEventParam->Reset();
+  return CJS_Return(true);
+}
+
+void CJX_EventPseudoModel::Property(CFXJSE_Value* pValue,
+                                    XFA_Event dwFlag,
+                                    bool bSetting) {
+  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
+  if (!pScriptContext)
+    return;
+
+  CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
+  if (!pEventParam)
+    return;
+
+  switch (dwFlag) {
+    case XFA_Event::CancelAction:
+      BooleanProperty(pValue, pEventParam->m_bCancelAction, bSetting);
+      break;
+    case XFA_Event::Change:
+      StringProperty(pValue, pEventParam->m_wsChange, bSetting);
+      break;
+    case XFA_Event::CommitKey:
+      InterProperty(pValue, pEventParam->m_iCommitKey, bSetting);
+      break;
+    case XFA_Event::FullText:
+      StringProperty(pValue, pEventParam->m_wsFullText, bSetting);
+      break;
+    case XFA_Event::Keydown:
+      BooleanProperty(pValue, pEventParam->m_bKeyDown, bSetting);
+      break;
+    case XFA_Event::Modifier:
+      BooleanProperty(pValue, pEventParam->m_bModifier, bSetting);
+      break;
+    case XFA_Event::NewContentType:
+      StringProperty(pValue, pEventParam->m_wsNewContentType, bSetting);
+      break;
+    case XFA_Event::NewText:
+      StringProperty(pValue, pEventParam->m_wsNewText, bSetting);
+      break;
+    case XFA_Event::PreviousContentType:
+      StringProperty(pValue, pEventParam->m_wsPrevContentType, bSetting);
+      break;
+    case XFA_Event::PreviousText:
+      StringProperty(pValue, pEventParam->m_wsPrevText, bSetting);
+      break;
+    case XFA_Event::Reenter:
+      BooleanProperty(pValue, pEventParam->m_bReenter, bSetting);
+      break;
+    case XFA_Event::SelectionEnd:
+      InterProperty(pValue, pEventParam->m_iSelEnd, bSetting);
+      break;
+    case XFA_Event::SelectionStart:
+      InterProperty(pValue, pEventParam->m_iSelStart, bSetting);
+      break;
+    case XFA_Event::Shift:
+      BooleanProperty(pValue, pEventParam->m_bShift, bSetting);
+      break;
+    case XFA_Event::SoapFaultCode:
+      StringProperty(pValue, pEventParam->m_wsSoapFaultCode, bSetting);
+      break;
+    case XFA_Event::SoapFaultString:
+      StringProperty(pValue, pEventParam->m_wsSoapFaultString, bSetting);
+      break;
+    case XFA_Event::Target:
+      break;
+    default:
+      break;
+  }
+}
diff --git a/fxjs/xfa/cjx_eventpseudomodel.h b/fxjs/xfa/cjx_eventpseudomodel.h
new file mode 100644
index 0000000..d9615c3
--- /dev/null
+++ b/fxjs/xfa/cjx_eventpseudomodel.h
@@ -0,0 +1,75 @@
+// Copyright 2017 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_CJX_EVENTPSEUDOMODEL_H_
+#define FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_
+
+#include "fxjs/CJX_Define.h"
+#include "fxjs/xfa/cjx_object.h"
+
+class CFXJSE_Value;
+class CScript_EventPseudoModel;
+
+enum class XFA_Event {
+  Change = 0,
+  CommitKey,
+  FullText,
+  Keydown,
+  Modifier,
+  NewContentType,
+  NewText,
+  PreviousContentType,
+  PreviousText,
+  Reenter,
+  SelectionEnd,
+  SelectionStart,
+  Shift,
+  SoapFaultCode,
+  SoapFaultString,
+  Target,
+  CancelAction
+};
+
+class CJX_EventPseudoModel : public CJX_Object {
+ public:
+  explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model);
+  ~CJX_EventPseudoModel() override;
+
+  void Change(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void CommitKey(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void FullText(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void KeyDown(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void Modifier(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void NewContentType(CFXJSE_Value* pValue,
+                      bool bSetting,
+                      XFA_Attribute eAttribute);
+  void NewText(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void PrevContentType(CFXJSE_Value* pValue,
+                       bool bSetting,
+                       XFA_Attribute eAttribute);
+  void PrevText(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void Reenter(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void SelEnd(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void SelStart(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void Shift(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void SoapFaultCode(CFXJSE_Value* pValue,
+                     bool bSetting,
+                     XFA_Attribute eAttribute);
+  void SoapFaultString(CFXJSE_Value* pValue,
+                       bool bSetting,
+                       XFA_Attribute eAttribute);
+  void Target(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+
+  JS_METHOD(emit, CJX_EventPseudoModel);
+  JS_METHOD(reset, CJX_EventPseudoModel);
+
+ private:
+  void Property(CFXJSE_Value* pValue, XFA_Event dwFlag, bool bSetting);
+
+  static const CJX_MethodSpec MethodSpecs[];
+};
+
+#endif  // FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_exclgroup.h b/fxjs/xfa/cjx_exclgroup.h
index 2ef280e..f444467 100644
--- a/fxjs/xfa/cjx_exclgroup.h
+++ b/fxjs/xfa/cjx_exclgroup.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_EXCLGROUP_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_ExclGroup;
 
diff --git a/fxjs/xfa/cjx_hostpseudomodel.cpp b/fxjs/xfa/cjx_hostpseudomodel.cpp
new file mode 100644
index 0000000..20e3bfe
--- /dev/null
+++ b/fxjs/xfa/cjx_hostpseudomodel.cpp
@@ -0,0 +1,639 @@
+// Copyright 2017 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/cjx_hostpseudomodel.h"
+
+#include <memory>
+#include <vector>
+
+#include "fxjs/cfxjse_engine.h"
+#include "fxjs/cfxjse_value.h"
+#include "fxjs/js_resources.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
+#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
+
+namespace {
+
+int32_t FilterName(const WideStringView& wsExpression,
+                   int32_t nStart,
+                   WideString& wsFilter) {
+  ASSERT(nStart > -1);
+  int32_t iLength = wsExpression.GetLength();
+  if (nStart >= iLength)
+    return iLength;
+
+  wchar_t* pBuf = wsFilter.GetBuffer(iLength - nStart);
+  int32_t nCount = 0;
+  const wchar_t* pSrc = wsExpression.unterminated_c_str();
+  wchar_t wCur;
+  while (nStart < iLength) {
+    wCur = pSrc[nStart++];
+    if (wCur == ',')
+      break;
+
+    pBuf[nCount++] = wCur;
+  }
+  wsFilter.ReleaseBuffer(nCount);
+  wsFilter.Trim();
+  return nStart;
+}
+
+}  // namespace
+
+const CJX_MethodSpec CJX_HostPseudoModel::MethodSpecs[] = {
+    {"beep", beep_static},
+    {"documentCountInBatch", documentCountInBatch_static},
+    {"documentInBatch", documentInBatch_static},
+    {"exportData", exportData_static},
+    {"getFocus", getFocus_static},
+    {"gotoURL", gotoURL_static},
+    {"importData", importData_static},
+    {"messageBox", messageBox_static},
+    {"openList", openList_static},
+    {"pageDown", pageDown_static},
+    {"pageUp", pageUp_static},
+    {"print", print_static},
+    {"resetData", resetData_static},
+    {"response", response_static},
+    {"setFocus", setFocus_static},
+    {"", nullptr}};
+
+CJX_HostPseudoModel::CJX_HostPseudoModel(CScript_HostPseudoModel* model)
+    : CJX_Object(model) {
+  DefineMethods(MethodSpecs);
+}
+
+CJX_HostPseudoModel::~CJX_HostPseudoModel() {}
+
+void CJX_HostPseudoModel::AppType(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetString("Exchange");
+}
+
+void CJX_HostPseudoModel::CalculationsEnabled(CFXJSE_Value* pValue,
+                                              bool bSetting,
+                                              XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  if (bSetting) {
+    pNotify->GetDocEnvironment()->SetCalculationsEnabled(hDoc,
+                                                         pValue->ToBoolean());
+    return;
+  }
+  pValue->SetBoolean(pNotify->GetDocEnvironment()->IsCalculationsEnabled(hDoc));
+}
+
+void CJX_HostPseudoModel::CurrentPage(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  if (bSetting) {
+    pNotify->GetDocEnvironment()->SetCurrentPage(hDoc, pValue->ToInteger());
+    return;
+  }
+  pValue->SetInteger(pNotify->GetDocEnvironment()->GetCurrentPage(hDoc));
+}
+
+void CJX_HostPseudoModel::Language(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  if (bSetting) {
+    ThrowException(L"Unable to set language value.");
+    return;
+  }
+  pValue->SetString(
+      pNotify->GetAppProvider()->GetLanguage().UTF8Encode().AsStringView());
+}
+
+void CJX_HostPseudoModel::NumPages(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  if (bSetting) {
+    ThrowException(L"Unable to set numPages value.");
+    return;
+  }
+  pValue->SetInteger(pNotify->GetDocEnvironment()->CountPages(hDoc));
+}
+
+void CJX_HostPseudoModel::Platform(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  if (bSetting) {
+    ThrowException(L"Unable to set platform value.");
+    return;
+  }
+  pValue->SetString(
+      pNotify->GetAppProvider()->GetPlatform().UTF8Encode().AsStringView());
+}
+
+void CJX_HostPseudoModel::Title(CFXJSE_Value* pValue,
+                                bool bSetting,
+                                XFA_Attribute eAttribute) {
+  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+    return;
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  if (bSetting) {
+    pNotify->GetDocEnvironment()->SetTitle(hDoc, pValue->ToWideString());
+    return;
+  }
+
+  WideString wsTitle;
+  pNotify->GetDocEnvironment()->GetTitle(hDoc, wsTitle);
+  pValue->SetString(wsTitle.UTF8Encode().AsStringView());
+}
+
+void CJX_HostPseudoModel::ValidationsEnabled(CFXJSE_Value* pValue,
+                                             bool bSetting,
+                                             XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  if (bSetting) {
+    pNotify->GetDocEnvironment()->SetValidationsEnabled(hDoc,
+                                                        pValue->ToBoolean());
+    return;
+  }
+
+  bool bEnabled = pNotify->GetDocEnvironment()->IsValidationsEnabled(hDoc);
+  pValue->SetBoolean(bEnabled);
+}
+
+void CJX_HostPseudoModel::Variation(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+    return;
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  if (bSetting) {
+    ThrowException(L"Unable to set variation value.");
+    return;
+  }
+  pValue->SetString("Full");
+}
+
+void CJX_HostPseudoModel::Version(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  if (bSetting) {
+    ThrowException(L"Unable to set version value.");
+    return;
+  }
+  pValue->SetString("11");
+}
+
+void CJX_HostPseudoModel::Name(CFXJSE_Value* pValue,
+                               bool bSetting,
+                               XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetString(
+      pNotify->GetAppProvider()->GetAppName().UTF8Encode().AsStringView());
+}
+
+CJS_Return CJX_HostPseudoModel::gotoURL(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+    return CJS_Return(true);
+
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  WideString URL = runtime->ToWideString(params[0]);
+  pNotify->GetDocEnvironment()->GotoURL(hDoc, URL);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::openList(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+    return CJS_Return(true);
+
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  CXFA_Node* pNode = nullptr;
+  if (params[0]->IsObject()) {
+    pNode = ToNode(runtime->ToXFAObject(params[0]));
+  } else if (params[0]->IsString()) {
+    CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
+    if (!pScriptContext)
+      return CJS_Return(true);
+
+    CXFA_Object* pObject = pScriptContext->GetThisObject();
+    if (!pObject)
+      return CJS_Return(true);
+
+    uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
+                      XFA_RESOLVENODE_Siblings;
+    XFA_RESOLVENODE_RS resolveNodeRS;
+    bool iRet = pScriptContext->ResolveObjects(
+        pObject, runtime->ToWideString(params[0]).AsStringView(),
+        &resolveNodeRS, dwFlag, nullptr);
+    if (!iRet || !resolveNodeRS.objects.front()->IsNode())
+      return CJS_Return(true);
+
+    pNode = resolveNodeRS.objects.front()->AsNode();
+  }
+
+  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
+  if (!pDocLayout)
+    return CJS_Return(true);
+
+  CXFA_FFWidget* hWidget =
+      pNotify->GetHWidget(pDocLayout->GetLayoutItem(pNode));
+  if (!hWidget)
+    return CJS_Return(true);
+
+  pNotify->GetDocEnvironment()->SetFocusWidget(pNotify->GetHDOC(), hWidget);
+  pNotify->OpenDropDownList(hWidget);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::response(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 4)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  WideString question;
+  if (params.size() >= 1)
+    question = runtime->ToWideString(params[0]);
+
+  WideString title;
+  if (params.size() >= 2)
+    title = runtime->ToWideString(params[1]);
+
+  WideString defaultAnswer;
+  if (params.size() >= 3)
+    defaultAnswer = runtime->ToWideString(params[2]);
+
+  bool mark = false;
+  if (params.size() >= 4)
+    mark = runtime->ToInt32(params[3]) != 0;
+
+  WideString answer =
+      pNotify->GetAppProvider()->Response(question, title, defaultAnswer, mark);
+  return CJS_Return(runtime->NewString(answer.UTF8Encode().AsStringView()));
+}
+
+CJS_Return CJX_HostPseudoModel::documentInBatch(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(runtime->NewNumber(0));
+}
+
+CJS_Return CJX_HostPseudoModel::resetData(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() > 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  WideString expression;
+  if (params.size() >= 1)
+    expression = runtime->ToWideString(params[0]);
+
+  if (expression.IsEmpty()) {
+    pNotify->ResetData();
+    return CJS_Return(true);
+  }
+
+  int32_t iStart = 0;
+  WideString wsName;
+  CXFA_Node* pNode = nullptr;
+  int32_t iExpLength = expression.GetLength();
+  while (iStart < iExpLength) {
+    iStart = FilterName(expression.AsStringView(), iStart, wsName);
+    CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
+    if (!pScriptContext)
+      return CJS_Return(true);
+
+    CXFA_Object* pObject = pScriptContext->GetThisObject();
+    if (!pObject)
+      return CJS_Return(true);
+
+    uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
+                      XFA_RESOLVENODE_Siblings;
+    XFA_RESOLVENODE_RS resolveNodeRS;
+    bool iRet = pScriptContext->ResolveObjects(pObject, wsName.AsStringView(),
+                                               &resolveNodeRS, dwFlag, nullptr);
+    if (!iRet || !resolveNodeRS.objects.front()->IsNode())
+      continue;
+
+    pNode = resolveNodeRS.objects.front()->AsNode();
+    pNotify->ResetData(pNode->GetWidgetData());
+  }
+  if (!pNode)
+    pNotify->ResetData();
+
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::beep(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+    return CJS_Return(true);
+
+  if (params.size() > 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  uint32_t dwType = 4;
+  if (params.size() >= 1)
+    dwType = runtime->ToInt32(params[0]);
+
+  pNotify->GetAppProvider()->Beep(dwType);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::setFocus(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+    return CJS_Return(true);
+
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  CXFA_Node* pNode = nullptr;
+  if (params.size() >= 1) {
+    if (params[0]->IsObject()) {
+      pNode = ToNode(runtime->ToXFAObject(params[0]));
+    } else if (params[0]->IsString()) {
+      CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
+      if (!pScriptContext)
+        return CJS_Return(true);
+
+      CXFA_Object* pObject = pScriptContext->GetThisObject();
+      if (!pObject)
+        return CJS_Return(true);
+
+      uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent |
+                        XFA_RESOLVENODE_Siblings;
+      XFA_RESOLVENODE_RS resolveNodeRS;
+      bool iRet = pScriptContext->ResolveObjects(
+          pObject, runtime->ToWideString(params[0]).AsStringView(),
+          &resolveNodeRS, dwFlag, nullptr);
+      if (!iRet || !resolveNodeRS.objects.front()->IsNode())
+        return CJS_Return(true);
+
+      pNode = resolveNodeRS.objects.front()->AsNode();
+    }
+  }
+  pNotify->SetFocusWidgetNode(pNode);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::getFocus(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  CXFA_Node* pNode = pNotify->GetFocusWidgetNode();
+  if (!pNode)
+    return CJS_Return(true);
+
+  CFXJSE_Value* value =
+      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode);
+  if (!value)
+    return CJS_Return(runtime->NewNull());
+
+  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+}
+
+CJS_Return CJX_HostPseudoModel::messageBox(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+    return CJS_Return(true);
+
+  if (params.empty() || params.size() > 4)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  WideString message;
+  if (params.size() >= 1)
+    message = runtime->ToWideString(params[0]);
+
+  WideString title;
+  if (params.size() >= 2)
+    title = runtime->ToWideString(params[1]);
+
+  uint32_t messageType = XFA_MBICON_Error;
+  if (params.size() >= 3) {
+    messageType = runtime->ToInt32(params[2]);
+    if (messageType > XFA_MBICON_Status)
+      messageType = XFA_MBICON_Error;
+  }
+
+  uint32_t buttonType = XFA_MB_OK;
+  if (params.size() >= 4) {
+    buttonType = runtime->ToInt32(params[3]);
+    if (buttonType > XFA_MB_YesNoCancel)
+      buttonType = XFA_MB_OK;
+  }
+
+  int32_t iValue = pNotify->GetAppProvider()->MsgBox(message, title,
+                                                     messageType, buttonType);
+  return CJS_Return(runtime->NewNumber(iValue));
+}
+
+CJS_Return CJX_HostPseudoModel::documentCountInBatch(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(runtime->NewNumber(0));
+}
+
+CJS_Return CJX_HostPseudoModel::print(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+    return CJS_Return(true);
+
+  if (params.size() != 8)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  uint32_t dwOptions = 0;
+  if (runtime->ToBoolean(params[0]))
+    dwOptions |= XFA_PRINTOPT_ShowDialog;
+  if (runtime->ToBoolean(params[3]))
+    dwOptions |= XFA_PRINTOPT_CanCancel;
+  if (runtime->ToBoolean(params[4]))
+    dwOptions |= XFA_PRINTOPT_ShrinkPage;
+  if (runtime->ToBoolean(params[5]))
+    dwOptions |= XFA_PRINTOPT_AsImage;
+  if (runtime->ToBoolean(params[6]))
+    dwOptions |= XFA_PRINTOPT_ReverseOrder;
+  if (runtime->ToBoolean(params[7]))
+    dwOptions |= XFA_PRINTOPT_PrintAnnot;
+
+  int32_t nStartPage = runtime->ToInt32(params[1]);
+  int32_t nEndPage = runtime->ToInt32(params[2]);
+
+  pNotify->GetDocEnvironment()->Print(pNotify->GetHDOC(), nStartPage, nEndPage,
+                                      dwOptions);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::importData(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::exportData(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 2)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  WideString filePath;
+  if (params.size() >= 1)
+    filePath = runtime->ToWideString(params[0]);
+
+  bool XDP = true;
+  if (params.size() >= 2)
+    XDP = runtime->ToBoolean(params[1]);
+
+  pNotify->GetDocEnvironment()->ExportData(pNotify->GetHDOC(), filePath, XDP);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::pageUp(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  int32_t nCurPage = pNotify->GetDocEnvironment()->GetCurrentPage(hDoc);
+  int32_t nNewPage = 0;
+  if (nCurPage <= 1)
+    return CJS_Return(true);
+
+  nNewPage = nCurPage - 1;
+  pNotify->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_HostPseudoModel::pageDown(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  CXFA_FFDoc* hDoc = pNotify->GetHDOC();
+  int32_t nCurPage = pNotify->GetDocEnvironment()->GetCurrentPage(hDoc);
+  int32_t nPageCount = pNotify->GetDocEnvironment()->CountPages(hDoc);
+  if (!nPageCount || nCurPage == nPageCount)
+    return CJS_Return(true);
+
+  int32_t nNewPage = 0;
+  if (nCurPage >= nPageCount)
+    nNewPage = nPageCount - 1;
+  else
+    nNewPage = nCurPage + 1;
+
+  pNotify->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage);
+  return CJS_Return(true);
+}
diff --git a/fxjs/xfa/cjx_hostpseudomodel.h b/fxjs/xfa/cjx_hostpseudomodel.h
new file mode 100644
index 0000000..fa3277a
--- /dev/null
+++ b/fxjs/xfa/cjx_hostpseudomodel.h
@@ -0,0 +1,60 @@
+// Copyright 2017 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_CJX_HOSTPSEUDOMODEL_H_
+#define FXJS_XFA_CJX_HOSTPSEUDOMODEL_H_
+
+#include "fxjs/CJX_Define.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+class CFXJSE_Value;
+class CScript_HostPseudoModel;
+
+class CJX_HostPseudoModel : public CJX_Object {
+ public:
+  explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model);
+  ~CJX_HostPseudoModel() override;
+
+  void AppType(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void CalculationsEnabled(CFXJSE_Value* pValue,
+                           bool bSetting,
+                           XFA_Attribute eAttribute);
+  void CurrentPage(CFXJSE_Value* pValue,
+                   bool bSetting,
+                   XFA_Attribute eAttribute);
+  void Language(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void NumPages(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void Platform(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void Title(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void ValidationsEnabled(CFXJSE_Value* pValue,
+                          bool bSetting,
+                          XFA_Attribute eAttribute);
+  void Variation(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void Version(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+  void Name(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+
+  JS_METHOD(beep, CJX_HostPseudoModel);
+  JS_METHOD(documentCountInBatch, CJX_HostPseudoModel);
+  JS_METHOD(documentInBatch, CJX_HostPseudoModel);
+  JS_METHOD(exportData, CJX_HostPseudoModel);
+  JS_METHOD(getFocus, CJX_HostPseudoModel);
+  JS_METHOD(gotoURL, CJX_HostPseudoModel);
+  JS_METHOD(importData, CJX_HostPseudoModel);
+  JS_METHOD(messageBox, CJX_HostPseudoModel);
+  JS_METHOD(openList, CJX_HostPseudoModel);
+  JS_METHOD(pageDown, CJX_HostPseudoModel);
+  JS_METHOD(pageUp, CJX_HostPseudoModel);
+  JS_METHOD(print, CJX_HostPseudoModel);
+  JS_METHOD(resetData, CJX_HostPseudoModel);
+  JS_METHOD(response, CJX_HostPseudoModel);
+  JS_METHOD(setFocus, CJX_HostPseudoModel);
+
+ private:
+  static const CJX_MethodSpec MethodSpecs[];
+};
+
+#endif  // FXJS_XFA_CJX_HOSTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_instancemanager.h b/fxjs/xfa/cjx_instancemanager.h
index a312366..1fd41a6 100644
--- a/fxjs/xfa/cjx_instancemanager.h
+++ b/fxjs/xfa/cjx_instancemanager.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_INSTANCEMANAGER_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_InstanceManager;
 
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.cpp b/fxjs/xfa/cjx_layoutpseudomodel.cpp
new file mode 100644
index 0000000..b80000a
--- /dev/null
+++ b/fxjs/xfa/cjx_layoutpseudomodel.cpp
@@ -0,0 +1,481 @@
+// Copyright 2017 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/cjx_layoutpseudomodel.h"
+
+#include <set>
+
+#include "core/fxcrt/fx_coordinates.h"
+#include "fxjs/cfxjse_engine.h"
+#include "fxjs/cfxjse_value.h"
+#include "fxjs/js_resources.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
+#include "xfa/fxfa/parser/cxfa_arraynodelist.h"
+#include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_layoutitem.h"
+#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
+#include "xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h"
+
+const CJX_MethodSpec CJX_LayoutPseudoModel::MethodSpecs[] = {
+    {"absPage", absPage_static},
+    {"absPageCount", absPageCount_static},
+    {"absPageCountInBatch", absPageCountInBatch_static},
+    {"absPageInBatch", absPageInBatch_static},
+    {"absPageSpan", absPageSpan_static},
+    {"h", h_static},
+    {"page", page_static},
+    {"pageContent", pageContent_static},
+    {"pageCount", pageCount_static},
+    {"pageSpan", pageSpan_static},
+    {"relayout", relayout_static},
+    {"relayoutPageArea", relayoutPageArea_static},
+    {"sheet", sheet_static},
+    {"sheetCount", sheetCount_static},
+    {"sheetCountInBatch", sheetCountInBatch_static},
+    {"sheetInBatch", sheetInBatch_static},
+    {"w", w_static},
+    {"x", x_static},
+    {"y", y_static},
+    {"", nullptr}};
+
+CJX_LayoutPseudoModel::CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model)
+    : CJX_Object(model) {
+  DefineMethods(MethodSpecs);
+}
+
+CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {}
+
+void CJX_LayoutPseudoModel::Ready(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+  if (bSetting) {
+    ThrowException(L"Unable to set ready value.");
+    return;
+  }
+
+  int32_t iStatus = pNotify->GetLayoutStatus();
+  pValue->SetBoolean(iStatus >= 2);
+}
+
+CJS_Return CJX_LayoutPseudoModel::HWXY(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params,
+    XFA_LAYOUTMODEL_HWXY layoutModel) {
+  if (params.empty() || params.size() > 3)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
+  if (!pNode)
+    return CJS_Return(true);
+
+  WideString unit(L"pt");
+  if (params.size() >= 2) {
+    WideString tmp_unit = runtime->ToWideString(params[1]);
+    if (!tmp_unit.IsEmpty())
+      unit = tmp_unit;
+  }
+  int32_t iIndex = params.size() >= 3 ? runtime->ToInt32(params[2]) : 0;
+
+  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
+  if (!pDocLayout)
+    return CJS_Return(true);
+
+  CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
+  if (!pLayoutItem)
+    return CJS_Return(true);
+
+  while (iIndex > 0 && pLayoutItem) {
+    pLayoutItem = pLayoutItem->GetNext();
+    --iIndex;
+  }
+
+  if (!pLayoutItem)
+    return CJS_Return(runtime->NewNumber(0.0));
+
+  CXFA_Measurement measure;
+  CFX_RectF rtRect = pLayoutItem->GetRect(true);
+  switch (layoutModel) {
+    case XFA_LAYOUTMODEL_H:
+      measure.Set(rtRect.height, XFA_Unit::Pt);
+      break;
+    case XFA_LAYOUTMODEL_W:
+      measure.Set(rtRect.width, XFA_Unit::Pt);
+      break;
+    case XFA_LAYOUTMODEL_X:
+      measure.Set(rtRect.left, XFA_Unit::Pt);
+      break;
+    case XFA_LAYOUTMODEL_Y:
+      measure.Set(rtRect.top, XFA_Unit::Pt);
+      break;
+  }
+
+  float fValue =
+      measure.ToUnit(CXFA_Measurement::GetUnitFromString(unit.AsStringView()));
+  return CJS_Return(runtime->NewNumber(FXSYS_round(fValue * 1000) / 1000.0f));
+}
+
+CJS_Return CJX_LayoutPseudoModel::h(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return HWXY(runtime, params, XFA_LAYOUTMODEL_H);
+}
+
+CJS_Return CJX_LayoutPseudoModel::w(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return HWXY(runtime, params, XFA_LAYOUTMODEL_W);
+}
+
+CJS_Return CJX_LayoutPseudoModel::x(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return HWXY(runtime, params, XFA_LAYOUTMODEL_X);
+}
+
+CJS_Return CJX_LayoutPseudoModel::y(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return HWXY(runtime, params, XFA_LAYOUTMODEL_Y);
+}
+
+CJS_Return CJX_LayoutPseudoModel::NumberedPageCount(CJS_V8* runtime,
+                                                    bool bNumbered) {
+  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
+  if (!pDocLayout)
+    return CJS_Return(true);
+
+  int32_t iPageCount = 0;
+  int32_t iPageNum = pDocLayout->CountPages();
+  if (bNumbered) {
+    for (int32_t i = 0; i < iPageNum; i++) {
+      CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
+      if (!pLayoutPage)
+        continue;
+
+      CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
+      if (pMasterPage->JSNode()->GetInteger(XFA_Attribute::Numbered))
+        iPageCount++;
+    }
+  } else {
+    iPageCount = iPageNum;
+  }
+  return CJS_Return(runtime->NewNumber(iPageCount));
+}
+
+CJS_Return CJX_LayoutPseudoModel::pageCount(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return NumberedPageCount(runtime, true);
+}
+
+CJS_Return CJX_LayoutPseudoModel::pageSpan(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
+  if (!pNode)
+    return CJS_Return(true);
+
+  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
+  if (!pDocLayout)
+    return CJS_Return(true);
+
+  CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
+  if (!pLayoutItem)
+    return CJS_Return(runtime->NewNumber(-1));
+
+  int32_t iLast = pLayoutItem->GetLast()->GetPage()->GetPageIndex();
+  int32_t iFirst = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
+  int32_t iPageSpan = iLast - iFirst + 1;
+  return CJS_Return(runtime->NewNumber(iPageSpan));
+}
+
+CJS_Return CJX_LayoutPseudoModel::page(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return PageInternals(runtime, params, false);
+}
+
+std::vector<CXFA_Node*> CJX_LayoutPseudoModel::GetObjArray(
+    CXFA_LayoutProcessor* pDocLayout,
+    int32_t iPageNo,
+    const WideString& wsType,
+    bool bOnPageArea) {
+  CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo);
+  if (!pLayoutPage)
+    return std::vector<CXFA_Node*>();
+
+  std::vector<CXFA_Node*> retArray;
+  if (wsType == L"pageArea") {
+    if (pLayoutPage->m_pFormNode)
+      retArray.push_back(pLayoutPage->m_pFormNode);
+    return retArray;
+  }
+  if (wsType == L"contentArea") {
+    for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
+         pItem = pItem->m_pNextSibling) {
+      if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea)
+        retArray.push_back(pItem->m_pFormNode);
+    }
+    return retArray;
+  }
+  std::set<CXFA_Node*> formItems;
+  if (wsType.IsEmpty()) {
+    if (pLayoutPage->m_pFormNode)
+      retArray.push_back(pLayoutPage->m_pFormNode);
+
+    for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
+         pItem = pItem->m_pNextSibling) {
+      if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) {
+        retArray.push_back(pItem->m_pFormNode);
+        if (!bOnPageArea) {
+          CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
+                                    CXFA_TraverseStrategy_ContentLayoutItem>
+          iterator(static_cast<CXFA_ContentLayoutItem*>(pItem->m_pFirstChild));
+          for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
+               pItemChild; pItemChild = iterator.MoveToNext()) {
+            if (!pItemChild->IsContentLayoutItem()) {
+              continue;
+            }
+            XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
+            if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
+                eType != XFA_Element::Subform && eType != XFA_Element::Area) {
+              continue;
+            }
+            if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
+              continue;
+
+            formItems.insert(pItemChild->m_pFormNode);
+            retArray.push_back(pItemChild->m_pFormNode);
+          }
+        }
+      } else {
+        if (bOnPageArea) {
+          CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
+                                    CXFA_TraverseStrategy_ContentLayoutItem>
+          iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
+          for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
+               pItemChild; pItemChild = iterator.MoveToNext()) {
+            if (!pItemChild->IsContentLayoutItem()) {
+              continue;
+            }
+            XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
+            if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
+                eType != XFA_Element::Subform && eType != XFA_Element::Area) {
+              continue;
+            }
+            if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
+              continue;
+            formItems.insert(pItemChild->m_pFormNode);
+            retArray.push_back(pItemChild->m_pFormNode);
+          }
+        }
+      }
+    }
+    return retArray;
+  }
+
+  XFA_Element eType = XFA_Element::Unknown;
+  if (wsType == L"field")
+    eType = XFA_Element::Field;
+  else if (wsType == L"draw")
+    eType = XFA_Element::Draw;
+  else if (wsType == L"subform")
+    eType = XFA_Element::Subform;
+  else if (wsType == L"area")
+    eType = XFA_Element::Area;
+
+  if (eType != XFA_Element::Unknown) {
+    for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
+         pItem = pItem->m_pNextSibling) {
+      if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) {
+        if (!bOnPageArea) {
+          CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
+                                    CXFA_TraverseStrategy_ContentLayoutItem>
+          iterator(static_cast<CXFA_ContentLayoutItem*>(pItem->m_pFirstChild));
+          for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
+               pItemChild; pItemChild = iterator.MoveToNext()) {
+            if (!pItemChild->IsContentLayoutItem())
+              continue;
+            if (pItemChild->m_pFormNode->GetElementType() != eType)
+              continue;
+            if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
+              continue;
+
+            formItems.insert(pItemChild->m_pFormNode);
+            retArray.push_back(pItemChild->m_pFormNode);
+          }
+        }
+      } else {
+        if (bOnPageArea) {
+          CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
+                                    CXFA_TraverseStrategy_ContentLayoutItem>
+          iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
+          for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
+               pItemChild; pItemChild = iterator.MoveToNext()) {
+            if (!pItemChild->IsContentLayoutItem())
+              continue;
+            if (pItemChild->m_pFormNode->GetElementType() != eType)
+              continue;
+            if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
+              continue;
+
+            formItems.insert(pItemChild->m_pFormNode);
+            retArray.push_back(pItemChild->m_pFormNode);
+          }
+        }
+      }
+    }
+  }
+  return retArray;
+}
+
+CJS_Return CJX_LayoutPseudoModel::pageContent(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 3)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  int32_t iIndex = 0;
+  if (params.size() >= 1)
+    iIndex = runtime->ToInt32(params[0]);
+
+  WideString wsType;
+  if (params.size() >= 2)
+    wsType = runtime->ToWideString(params[1]);
+
+  bool bOnPageArea = false;
+  if (params.size() >= 3)
+    bOnPageArea = runtime->ToBoolean(params[2]);
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return CJS_Return(true);
+
+  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
+  if (!pDocLayout)
+    return CJS_Return(true);
+
+  auto pArrayNodeList = pdfium::MakeUnique<CXFA_ArrayNodeList>(GetDocument());
+  pArrayNodeList->SetArrayNodeList(
+      GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea));
+
+  // TODO(dsinclair): Who owns the array once we release it? Won't this leak?
+  return CJS_Return(runtime->NewXFAObject(
+      pArrayNodeList.release(),
+      GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
+}
+
+CJS_Return CJX_LayoutPseudoModel::absPageCount(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return NumberedPageCount(runtime, false);
+}
+
+CJS_Return CJX_LayoutPseudoModel::absPageCountInBatch(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(runtime->NewNumber(0));
+}
+
+CJS_Return CJX_LayoutPseudoModel::sheetCountInBatch(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(runtime->NewNumber(0));
+}
+
+CJS_Return CJX_LayoutPseudoModel::relayout(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  CXFA_Node* pRootNode = GetDocument()->GetRoot();
+  CXFA_Node* pFormRoot = pRootNode->GetFirstChildByClass(XFA_Element::Form);
+  CXFA_Node* pContentRootNode = pFormRoot->GetNodeItem(XFA_NODEITEM_FirstChild);
+  CXFA_LayoutProcessor* pLayoutProcessor = GetDocument()->GetLayoutProcessor();
+  if (pContentRootNode)
+    pLayoutProcessor->AddChangedContainer(pContentRootNode);
+
+  pLayoutProcessor->SetForceReLayout(true);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_LayoutPseudoModel::absPageSpan(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return pageSpan(runtime, params);
+}
+
+CJS_Return CJX_LayoutPseudoModel::absPageInBatch(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  return CJS_Return(runtime->NewNumber(0));
+}
+
+CJS_Return CJX_LayoutPseudoModel::sheetInBatch(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  return CJS_Return(runtime->NewNumber(0));
+}
+
+CJS_Return CJX_LayoutPseudoModel::sheet(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return PageInternals(runtime, params, true);
+}
+
+CJS_Return CJX_LayoutPseudoModel::relayoutPageArea(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_LayoutPseudoModel::sheetCount(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return NumberedPageCount(runtime, false);
+}
+
+CJS_Return CJX_LayoutPseudoModel::absPage(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return PageInternals(runtime, params, true);
+}
+
+CJS_Return CJX_LayoutPseudoModel::PageInternals(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params,
+    bool bAbsPage) {
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
+  if (!pNode)
+    return CJS_Return(runtime->NewNumber(0));
+
+  CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
+  if (!pDocLayout)
+    return CJS_Return(true);
+
+  CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
+  if (!pLayoutItem)
+    return CJS_Return(runtime->NewNumber(-1));
+
+  int32_t iPage = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
+  return CJS_Return(runtime->NewNumber(bAbsPage ? iPage : iPage + 1));
+}
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.h b/fxjs/xfa/cjx_layoutpseudomodel.h
new file mode 100644
index 0000000..63dc789
--- /dev/null
+++ b/fxjs/xfa/cjx_layoutpseudomodel.h
@@ -0,0 +1,70 @@
+// Copyright 2017 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_CJX_LAYOUTPSEUDOMODEL_H_
+#define FXJS_XFA_CJX_LAYOUTPSEUDOMODEL_H_
+
+#include <vector>
+
+#include "fxjs/CJX_Define.h"
+#include "fxjs/xfa/cjx_object.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 : public CJX_Object {
+ public:
+  explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model);
+  ~CJX_LayoutPseudoModel() override;
+
+  void Ready(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute);
+
+  JS_METHOD(absPage, CJX_LayoutPseudoModel);
+  JS_METHOD(absPageCount, CJX_LayoutPseudoModel);
+  JS_METHOD(absPageCountInBatch, CJX_LayoutPseudoModel);
+  JS_METHOD(absPageInBatch, CJX_LayoutPseudoModel);
+  JS_METHOD(absPageSpan, CJX_LayoutPseudoModel);
+  JS_METHOD(h, CJX_LayoutPseudoModel);
+  JS_METHOD(page, CJX_LayoutPseudoModel);
+  JS_METHOD(pageContent, CJX_LayoutPseudoModel);
+  JS_METHOD(pageCount, CJX_LayoutPseudoModel);
+  JS_METHOD(pageSpan, CJX_LayoutPseudoModel);
+  JS_METHOD(relayout, CJX_LayoutPseudoModel);
+  JS_METHOD(relayoutPageArea, CJX_LayoutPseudoModel);
+  JS_METHOD(sheet, CJX_LayoutPseudoModel);
+  JS_METHOD(sheetCount, CJX_LayoutPseudoModel);
+  JS_METHOD(sheetCountInBatch, CJX_LayoutPseudoModel);
+  JS_METHOD(sheetInBatch, CJX_LayoutPseudoModel);
+  JS_METHOD(w, CJX_LayoutPseudoModel);
+  JS_METHOD(x, CJX_LayoutPseudoModel);
+  JS_METHOD(y, CJX_LayoutPseudoModel);
+
+ private:
+  CJS_Return NumberedPageCount(CJS_V8* runtime, bool bNumbered);
+  CJS_Return HWXY(CJS_V8* runtime,
+                  const std::vector<v8::Local<v8::Value>>& params,
+                  XFA_LAYOUTMODEL_HWXY layoutModel);
+  std::vector<CXFA_Node*> GetObjArray(CXFA_LayoutProcessor* pDocLayout,
+                                      int32_t iPageNo,
+                                      const WideString& wsType,
+                                      bool bOnPageArea);
+  CJS_Return PageInternals(CJS_V8* runtime,
+                           const std::vector<v8::Local<v8::Value>>& params,
+                           bool bAbsPage);
+
+  static const CJX_MethodSpec MethodSpecs[];
+};
+
+#endif  // FXJS_XFA_CJX_LAYOUTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_list.h b/fxjs/xfa/cjx_list.h
index b4a0025..45d827c 100644
--- a/fxjs/xfa/cjx_list.h
+++ b/fxjs/xfa/cjx_list.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_LIST_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_object.h"
+#include "fxjs/xfa/cjx_object.h"
 
 class CXFA_List;
 
diff --git a/fxjs/xfa/cjx_logpseudomodel.cpp b/fxjs/xfa/cjx_logpseudomodel.cpp
new file mode 100644
index 0000000..e0ed05d
--- /dev/null
+++ b/fxjs/xfa/cjx_logpseudomodel.cpp
@@ -0,0 +1,57 @@
+// Copyright 2017 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/cjx_logpseudomodel.h"
+
+#include <vector>
+
+#include "fxjs/cfxjse_value.h"
+#include "xfa/fxfa/parser/cscript_logpseudomodel.h"
+
+const CJX_MethodSpec CJX_LogPseudoModel::MethodSpecs[] = {
+    {"message", message_static},
+    {"traceEnabled", traceEnabled_static},
+    {"traceActivate", traceActivate_static},
+    {"traceDeactivate", traceDeactivate_static},
+    {"trace", trace_static},
+    {"", nullptr}};
+
+CJX_LogPseudoModel::CJX_LogPseudoModel(CScript_LogPseudoModel* model)
+    : CJX_Object(model) {
+  DefineMethods(MethodSpecs);
+}
+
+CJX_LogPseudoModel::~CJX_LogPseudoModel() {}
+
+CJS_Return CJX_LogPseudoModel::message(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_LogPseudoModel::traceEnabled(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_LogPseudoModel::traceActivate(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_LogPseudoModel::traceDeactivate(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_LogPseudoModel::trace(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  return CJS_Return(true);
+}
diff --git a/fxjs/xfa/cjx_logpseudomodel.h b/fxjs/xfa/cjx_logpseudomodel.h
new file mode 100644
index 0000000..2d98241
--- /dev/null
+++ b/fxjs/xfa/cjx_logpseudomodel.h
@@ -0,0 +1,30 @@
+// Copyright 2017 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_CJX_LOGPSEUDOMODEL_H_
+#define FXJS_XFA_CJX_LOGPSEUDOMODEL_H_
+
+#include "fxjs/CJX_Define.h"
+#include "fxjs/xfa/cjx_object.h"
+
+class CScript_LogPseudoModel;
+
+class CJX_LogPseudoModel : public CJX_Object {
+ public:
+  explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model);
+  ~CJX_LogPseudoModel() override;
+
+  JS_METHOD(message, CJX_LogPseudoModel);
+  JS_METHOD(traceEnabled, CJX_LogPseudoModel);
+  JS_METHOD(traceActivate, CJX_LogPseudoModel);
+  JS_METHOD(traceDeactivate, CJX_LogPseudoModel);
+  JS_METHOD(trace, CJX_LogPseudoModel);
+
+ private:
+  static const CJX_MethodSpec MethodSpecs[];
+};
+
+#endif  // FXJS_XFA_CJX_LOGPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_manifest.h b/fxjs/xfa/cjx_manifest.h
index 167ef46..fd28e40 100644
--- a/fxjs/xfa/cjx_manifest.h
+++ b/fxjs/xfa/cjx_manifest.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_MANIFEST_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_Manifest;
 
diff --git a/fxjs/xfa/cjx_model.h b/fxjs/xfa/cjx_model.h
index f4e6ac9..17b8d3d 100644
--- a/fxjs/xfa/cjx_model.h
+++ b/fxjs/xfa/cjx_model.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_MODEL_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_Node;
 
diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp
new file mode 100644
index 0000000..dd22fb4
--- /dev/null
+++ b/fxjs/xfa/cjx_node.cpp
@@ -0,0 +1,1874 @@
+// Copyright 2017 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/cjx_node.h"
+
+#include <map>
+#include <tuple>
+#include <vector>
+
+#include "core/fxcrt/cfx_decimal.h"
+#include "core/fxcrt/cfx_memorystream.h"
+#include "core/fxcrt/fx_codepage.h"
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "core/fxcrt/xml/cfx_xmlnode.h"
+#include "core/fxcrt/xml/cfx_xmltext.h"
+#include "fxjs/cfxjse_engine.h"
+#include "fxjs/js_resources.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/parser/cxfa_arraynodelist.h"
+#include "xfa/fxfa/parser/cxfa_attachnodelist.h"
+#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_occurdata.h"
+#include "xfa/fxfa/parser/cxfa_simple_parser.h"
+#include "xfa/fxfa/parser/xfa_utils.h"
+
+namespace {
+
+std::tuple<int32_t, int32_t, int32_t> StrToRGB(const WideString& strRGB) {
+  int32_t r = 0;
+  int32_t g = 0;
+  int32_t b = 0;
+
+  size_t iIndex = 0;
+  for (size_t i = 0; i < strRGB.GetLength(); ++i) {
+    wchar_t ch = strRGB[i];
+    if (ch == L',')
+      ++iIndex;
+    if (iIndex > 2)
+      break;
+
+    int32_t iValue = ch - L'0';
+    if (iValue >= 0 && iValue <= 9) {
+      switch (iIndex) {
+        case 0:
+          r = r * 10 + iValue;
+          break;
+        case 1:
+          g = g * 10 + iValue;
+          break;
+        default:
+          b = b * 10 + iValue;
+          break;
+      }
+    }
+  }
+  return {r, g, b};
+}
+
+enum class EventAppliesToo {
+  kNone = 0,
+  kAll = 1,
+  kAllNonRecursive = 2,
+  kSubform = 3,
+  kFieldOrExclusion = 4,
+  kField = 5,
+  kSignature = 6,
+  kChoiceList = 7
+};
+
+struct XFA_ExecEventParaInfo {
+ public:
+  uint32_t m_uHash;
+  const wchar_t* m_lpcEventName;
+  XFA_EVENTTYPE m_eventType;
+  EventAppliesToo m_validFlags;
+};
+
+const XFA_ExecEventParaInfo gs_eventParaInfos[] = {
+    {0x109d7ce7, L"mouseEnter", XFA_EVENT_MouseEnter, EventAppliesToo::kField},
+    {0x1bfc72d9, L"preOpen", XFA_EVENT_PreOpen, EventAppliesToo::kChoiceList},
+    {0x2196a452, L"initialize", XFA_EVENT_Initialize, EventAppliesToo::kAll},
+    {0x27410f03, L"mouseExit", XFA_EVENT_MouseExit, EventAppliesToo::kField},
+    {0x36f1c6d8, L"preSign", XFA_EVENT_PreSign, EventAppliesToo::kSignature},
+    {0x4731d6ba, L"exit", XFA_EVENT_Exit, EventAppliesToo::kAllNonRecursive},
+    {0x7233018a, L"validate", XFA_EVENT_Validate, EventAppliesToo::kAll},
+    {0x8808385e, L"indexChange", XFA_EVENT_IndexChange,
+     EventAppliesToo::kSubform},
+    {0x891f4606, L"change", XFA_EVENT_Change,
+     EventAppliesToo::kFieldOrExclusion},
+    {0x9f693b21, L"mouseDown", XFA_EVENT_MouseDown, EventAppliesToo::kField},
+    {0xcdce56b3, L"full", XFA_EVENT_Full, EventAppliesToo::kFieldOrExclusion},
+    {0xd576d08e, L"mouseUp", XFA_EVENT_MouseUp, EventAppliesToo::kField},
+    {0xd95657a6, L"click", XFA_EVENT_Click, EventAppliesToo::kFieldOrExclusion},
+    {0xdbfbe02e, L"calculate", XFA_EVENT_Calculate, EventAppliesToo::kAll},
+    {0xe25fa7b8, L"postOpen", XFA_EVENT_PostOpen, EventAppliesToo::kChoiceList},
+    {0xe28dce7e, L"enter", XFA_EVENT_Enter, EventAppliesToo::kAllNonRecursive},
+    {0xfd54fbb7, L"postSign", XFA_EVENT_PostSign, EventAppliesToo::kSignature},
+};
+
+const XFA_ExecEventParaInfo* GetEventParaInfoByName(
+    const WideStringView& wsEventName) {
+  uint32_t uHash = FX_HashCode_GetW(wsEventName, false);
+  int32_t iStart = 0;
+  int32_t iEnd = (sizeof(gs_eventParaInfos) / sizeof(gs_eventParaInfos[0])) - 1;
+  do {
+    int32_t iMid = (iStart + iEnd) / 2;
+    const XFA_ExecEventParaInfo* eventParaInfo = &gs_eventParaInfos[iMid];
+    if (uHash == eventParaInfo->m_uHash)
+      return eventParaInfo;
+    if (uHash < eventParaInfo->m_uHash)
+      iEnd = iMid - 1;
+    else
+      iStart = iMid + 1;
+  } while (iStart <= iEnd);
+  return nullptr;
+}
+
+}  // namespace
+
+const CJX_MethodSpec CJX_Node::MethodSpecs[] = {
+    {"applyXSL", applyXSL_static},
+    {"assignNode", assignNode_static},
+    {"clone", clone_static},
+    {"getAttribute", getAttribute_static},
+    {"getElement", getElement_static},
+    {"isPropertySpecified", isPropertySpecified_static},
+    {"loadXML", loadXML_static},
+    {"saveFilteredXML", saveFilteredXML_static},
+    {"saveXML", saveXML_static},
+    {"setAttribute", setAttribute_static},
+    {"setElement", setElement_static},
+    {"", nullptr}};
+
+CJX_Node::CJX_Node(CXFA_Node* node) : CJX_Tree(node) {
+  DefineMethods(MethodSpecs);
+}
+
+CJX_Node::~CJX_Node() = default;
+
+CXFA_Node* CJX_Node::GetXFANode() {
+  return static_cast<CXFA_Node*>(GetXFAObject());
+}
+
+const CXFA_Node* CJX_Node::GetXFANode() const {
+  return static_cast<const CXFA_Node*>(GetXFAObject());
+}
+
+int32_t CJX_Node::Subform_and_SubformSet_InstanceIndex() {
+  int32_t index = 0;
+  for (CXFA_Node* pNode = GetXFANode()->GetNodeItem(XFA_NODEITEM_PrevSibling);
+       pNode; pNode = pNode->GetNodeItem(XFA_NODEITEM_PrevSibling)) {
+    if ((pNode->GetElementType() != XFA_Element::Subform) &&
+        (pNode->GetElementType() != XFA_Element::SubformSet)) {
+      break;
+    }
+    index++;
+  }
+  return index;
+}
+
+int32_t CJX_Node::InstanceManager_SetInstances(int32_t iDesired) {
+  CXFA_OccurData occurData(GetXFANode()->GetOccurNode());
+  if (iDesired < occurData.GetMin()) {
+    ThrowTooManyOccurancesException(L"min");
+    return 1;
+  }
+
+  int32_t iMax = occurData.GetMax();
+  if (iMax >= 0 && iDesired > iMax) {
+    ThrowTooManyOccurancesException(L"max");
+    return 2;
+  }
+
+  int32_t iCount = GetXFANode()->GetCount();
+  if (iDesired == iCount)
+    return 0;
+
+  if (iDesired < iCount) {
+    WideString wsInstManagerName = GetCData(XFA_Attribute::Name);
+    WideString wsInstanceName = WideString(
+        wsInstManagerName.IsEmpty()
+            ? wsInstManagerName
+            : wsInstManagerName.Right(wsInstManagerName.GetLength() - 1));
+    uint32_t dInstanceNameHash =
+        FX_HashCode_GetW(wsInstanceName.AsStringView(), false);
+    CXFA_Node* pPrevSibling =
+        iDesired == 0 ? GetXFANode() : GetXFANode()->GetItem(iDesired - 1);
+    while (iCount > iDesired) {
+      CXFA_Node* pRemoveInstance =
+          pPrevSibling->GetNodeItem(XFA_NODEITEM_NextSibling);
+      if (pRemoveInstance->GetElementType() != XFA_Element::Subform &&
+          pRemoveInstance->GetElementType() != XFA_Element::SubformSet) {
+        continue;
+      }
+      if (pRemoveInstance->GetElementType() == XFA_Element::InstanceManager) {
+        NOTREACHED();
+        break;
+      }
+      if (pRemoveInstance->GetNameHash() == dInstanceNameHash) {
+        GetXFANode()->RemoveItem(pRemoveInstance, true);
+        iCount--;
+      }
+    }
+  } else {
+    while (iCount < iDesired) {
+      CXFA_Node* pNewInstance = GetXFANode()->CreateInstance(true);
+      GetXFANode()->InsertItem(pNewInstance, iCount, iCount, false);
+      iCount++;
+      CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+      if (!pNotify)
+        return 0;
+
+      pNotify->RunNodeInitialize(pNewInstance);
+    }
+  }
+
+  CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
+  if (pLayoutPro) {
+    pLayoutPro->AddChangedContainer(
+        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  }
+  return 0;
+}
+
+int32_t CJX_Node::InstanceManager_MoveInstance(int32_t iTo, int32_t iFrom) {
+  int32_t iCount = GetXFANode()->GetCount();
+  if (iFrom > iCount || iTo > iCount - 1) {
+    ThrowIndexOutOfBoundsException();
+    return 1;
+  }
+  if (iFrom < 0 || iTo < 0 || iFrom == iTo)
+    return 0;
+
+  CXFA_Node* pMoveInstance = GetXFANode()->GetItem(iFrom);
+  GetXFANode()->RemoveItem(pMoveInstance, false);
+  GetXFANode()->InsertItem(pMoveInstance, iTo, iCount - 1, true);
+  CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
+  if (pLayoutPro) {
+    pLayoutPro->AddChangedContainer(
+        ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form)));
+  }
+  return 0;
+}
+
+CJS_Return CJX_Node::applyXSL(CJS_V8* runtime,
+                              const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  // TODO(weili): check whether we need to implement this, pdfium:501.
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_Node::assignNode(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 3)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  // TODO(weili): check whether we need to implement this, pdfium:501.
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_Node::clone(CJS_V8* runtime,
+                           const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  CXFA_Node* pCloneNode = GetXFANode()->Clone(runtime->ToBoolean(params[0]));
+  CFXJSE_Value* value =
+      GetDocument()->GetScriptContext()->GetJSValueFromMap(pCloneNode);
+  if (!value)
+    return CJS_Return(runtime->NewNull());
+  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+}
+
+CJS_Return CJX_Node::getAttribute(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  WideString expression = runtime->ToWideString(params[0]);
+  return CJS_Return(runtime->NewString(
+      GetAttribute(expression.AsStringView()).UTF8Encode().AsStringView()));
+}
+
+CJS_Return CJX_Node::getElement(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 2)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  WideString expression = runtime->ToWideString(params[0]);
+  int32_t iValue = params.size() >= 2 ? runtime->ToInt32(params[1]) : 0;
+
+  CXFA_Node* pNode =
+      GetProperty(iValue, CXFA_Node::NameToElement(expression), true);
+  CFXJSE_Value* value =
+      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode);
+  if (!value)
+    return CJS_Return(runtime->NewNull());
+  return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate()));
+}
+
+CJS_Return CJX_Node::isPropertySpecified(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 3)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  WideString expression = runtime->ToWideString(params[0]);
+  XFA_Attribute attr = CXFA_Node::NameToAttribute(expression.AsStringView());
+  if (attr != XFA_Attribute::Unknown && HasAttribute(attr))
+    return CJS_Return(runtime->NewBoolean(true));
+
+  bool bParent = params.size() < 2 || runtime->ToBoolean(params[1]);
+  int32_t iIndex = params.size() == 3 ? runtime->ToInt32(params[2]) : 0;
+  XFA_Element eType = CXFA_Node::NameToElement(expression);
+  bool bHas = !!GetProperty(iIndex, eType, true);
+  if (!bHas && bParent && GetXFANode()->GetParent()) {
+    // Also check on the parent.
+    auto* jsnode = GetXFANode()->GetParent()->JSNode();
+    bHas = jsnode->HasAttribute(attr) ||
+           !!jsnode->GetProperty(iIndex, eType, true);
+  }
+  return CJS_Return(runtime->NewBoolean(bHas));
+}
+
+CJS_Return CJX_Node::loadXML(CJS_V8* runtime,
+                             const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 3)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  ByteString expression = runtime->ToByteString(params[0]);
+  if (expression.IsEmpty())
+    return CJS_Return(true);
+
+  bool bIgnoreRoot = true;
+  if (params.size() >= 2)
+    bIgnoreRoot = runtime->ToBoolean(params[1]);
+
+  bool bOverwrite = 0;
+  if (params.size() >= 3)
+    bOverwrite = runtime->ToBoolean(params[2]);
+
+  auto pParser = pdfium::MakeUnique<CXFA_SimpleParser>(GetDocument());
+  if (!pParser)
+    return CJS_Return(true);
+
+  CFX_XMLNode* pXMLNode = pParser->ParseXMLData(expression);
+  if (!pXMLNode)
+    return CJS_Return(true);
+
+  if (bIgnoreRoot &&
+      (pXMLNode->GetType() != FX_XMLNODE_Element ||
+       XFA_RecognizeRichText(static_cast<CFX_XMLElement*>(pXMLNode)))) {
+    bIgnoreRoot = false;
+  }
+
+  CXFA_Node* pFakeRoot = GetXFANode()->Clone(false);
+  WideString wsContentType = GetCData(XFA_Attribute::ContentType);
+  if (!wsContentType.IsEmpty()) {
+    pFakeRoot->JSNode()->SetCData(XFA_Attribute::ContentType,
+                                  WideString(wsContentType), false, false);
+  }
+
+  std::unique_ptr<CFX_XMLNode> pFakeXMLRoot(pFakeRoot->GetXMLMappingNode());
+  if (!pFakeXMLRoot) {
+    CFX_XMLNode* pThisXMLRoot = GetXFANode()->GetXMLMappingNode();
+    pFakeXMLRoot = pThisXMLRoot ? pThisXMLRoot->Clone() : nullptr;
+  }
+  if (!pFakeXMLRoot) {
+    pFakeXMLRoot = pdfium::MakeUnique<CFX_XMLElement>(
+        WideString(GetXFANode()->GetClassName()));
+  }
+
+  if (bIgnoreRoot) {
+    CFX_XMLNode* pXMLChild = pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild);
+    while (pXMLChild) {
+      CFX_XMLNode* pXMLSibling =
+          pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling);
+      pXMLNode->RemoveChildNode(pXMLChild);
+      pFakeXMLRoot->InsertChildNode(pXMLChild);
+      pXMLChild = pXMLSibling;
+    }
+  } else {
+    CFX_XMLNode* pXMLParent = pXMLNode->GetNodeItem(CFX_XMLNode::Parent);
+    if (pXMLParent)
+      pXMLParent->RemoveChildNode(pXMLNode);
+
+    pFakeXMLRoot->InsertChildNode(pXMLNode);
+  }
+
+  pParser->ConstructXFANode(pFakeRoot, pFakeXMLRoot.get());
+  pFakeRoot = pParser->GetRootNode();
+  if (!pFakeRoot)
+    return CJS_Return(true);
+
+  if (bOverwrite) {
+    CXFA_Node* pChild = GetXFANode()->GetNodeItem(XFA_NODEITEM_FirstChild);
+    CXFA_Node* pNewChild = pFakeRoot->GetNodeItem(XFA_NODEITEM_FirstChild);
+    int32_t index = 0;
+    while (pNewChild) {
+      CXFA_Node* pItem = pNewChild->GetNodeItem(XFA_NODEITEM_NextSibling);
+      pFakeRoot->RemoveChild(pNewChild, true);
+      GetXFANode()->InsertChild(index++, pNewChild);
+      pNewChild->SetFlag(XFA_NodeFlag_Initialized, true);
+      pNewChild = pItem;
+    }
+
+    while (pChild) {
+      CXFA_Node* pItem = pChild->GetNodeItem(XFA_NODEITEM_NextSibling);
+      GetXFANode()->RemoveChild(pChild, true);
+      pFakeRoot->InsertChild(pChild, nullptr);
+      pChild = pItem;
+    }
+
+    if (GetXFANode()->GetPacketType() == XFA_PacketType::Form &&
+        GetXFANode()->GetElementType() == XFA_Element::ExData) {
+      CFX_XMLNode* pTempXMLNode = GetXFANode()->GetXMLMappingNode();
+      GetXFANode()->SetXMLMappingNode(pFakeXMLRoot.release());
+      GetXFANode()->SetFlag(XFA_NodeFlag_OwnXMLNode, false);
+      if (pTempXMLNode && !pTempXMLNode->GetNodeItem(CFX_XMLNode::Parent))
+        pFakeXMLRoot.reset(pTempXMLNode);
+      else
+        pFakeXMLRoot = nullptr;
+    }
+    MoveBufferMapData(pFakeRoot, GetXFANode());
+  } else {
+    CXFA_Node* pChild = pFakeRoot->GetNodeItem(XFA_NODEITEM_FirstChild);
+    while (pChild) {
+      CXFA_Node* pItem = pChild->GetNodeItem(XFA_NODEITEM_NextSibling);
+      pFakeRoot->RemoveChild(pChild, true);
+      GetXFANode()->InsertChild(pChild, nullptr);
+      pChild->SetFlag(XFA_NodeFlag_Initialized, true);
+      pChild = pItem;
+    }
+  }
+
+  if (pFakeXMLRoot) {
+    pFakeRoot->SetXMLMappingNode(pFakeXMLRoot.release());
+    pFakeRoot->SetFlag(XFA_NodeFlag_OwnXMLNode, false);
+  }
+  pFakeRoot->SetFlag(XFA_NodeFlag_HasRemovedChildren, false);
+
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_Node::saveFilteredXML(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  // TODO(weili): Check whether we need to implement this, pdfium:501.
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_Node::saveXML(CJS_V8* runtime,
+                             const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() > 1)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  if (params.size() == 1 && runtime->ToWideString(params[0]) != L"pretty")
+    return CJS_Return(JSGetStringFromID(JSMessage::kValueError));
+
+  // TODO(weili): Check whether we need to save pretty print XML, pdfium:501.
+
+  WideString bsXMLHeader = L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+  if (GetXFANode()->GetPacketType() != XFA_PacketType::Form &&
+      GetXFANode()->GetPacketType() != XFA_PacketType::Datasets) {
+    return CJS_Return(runtime->NewString(""));
+  }
+
+  CFX_XMLNode* pElement = nullptr;
+  if (GetXFANode()->GetPacketType() == XFA_PacketType::Datasets) {
+    pElement = GetXFANode()->GetXMLMappingNode();
+    if (!pElement || pElement->GetType() != FX_XMLNODE_Element) {
+      return CJS_Return(
+          runtime->NewString(bsXMLHeader.UTF8Encode().AsStringView()));
+    }
+
+    XFA_DataExporter_DealWithDataGroupNode(GetXFANode());
+  }
+
+  auto pMemoryStream = pdfium::MakeRetain<CFX_MemoryStream>(true);
+  auto pStream =
+      pdfium::MakeRetain<CFX_SeekableStreamProxy>(pMemoryStream, true);
+  pStream->SetCodePage(FX_CODEPAGE_UTF8);
+  pStream->WriteString(bsXMLHeader.AsStringView());
+
+  if (GetXFANode()->GetPacketType() == XFA_PacketType::Form)
+    XFA_DataExporter_RegenerateFormFile(GetXFANode(), pStream, nullptr, true);
+  else
+    pElement->SaveXMLNode(pStream);
+
+  return CJS_Return(runtime->NewString(
+      ByteStringView(pMemoryStream->GetBuffer(), pMemoryStream->GetSize())));
+}
+
+CJS_Return CJX_Node::setAttribute(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 2)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  WideString attributeValue = runtime->ToWideString(params[0]);
+  WideString attribute = runtime->ToWideString(params[1]);
+  SetAttribute(attribute.AsStringView(), attributeValue.AsStringView(), true);
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_Node::setElement(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() != 1 && params.size() != 2)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+
+  // TODO(weili): check whether we need to implement this, pdfium:501.
+  return CJS_Return(true);
+}
+
+void CJX_Node::Script_NodeClass_Ns(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetString(
+      TryNamespace().value_or(WideString()).UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_NodeClass_Model(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+
+  pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(
+      GetXFANode()->GetModelNode()));
+}
+
+void CJX_Node::Script_NodeClass_IsContainer(CFXJSE_Value* pValue,
+                                            bool bSetting,
+                                            XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+
+  pValue->SetBoolean(GetXFANode()->IsContainerNode());
+}
+
+void CJX_Node::Script_NodeClass_IsNull(CFXJSE_Value* pValue,
+                                       bool bSetting,
+                                       XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  if (GetXFANode()->GetElementType() == XFA_Element::Subform) {
+    pValue->SetBoolean(false);
+    return;
+  }
+  pValue->SetBoolean(GetContent(false).IsEmpty());
+}
+
+void CJX_Node::Script_NodeClass_OneOfChild(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+
+  std::vector<CXFA_Node*> properties = GetXFANode()->GetNodeList(
+      XFA_NODEFILTER_OneOfProperty, XFA_Element::Unknown);
+  if (!properties.empty()) {
+    pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(
+        properties.front()));
+  }
+}
+
+void CJX_Node::Script_ModelClass_Context(CFXJSE_Value* pValue,
+                                         bool bSetting,
+                                         XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_ModelClass_AliasNode(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_Attribute_Integer(CFXJSE_Value* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute) {
+  if (bSetting) {
+    SetInteger(eAttribute, pValue->ToInteger(), true);
+    return;
+  }
+  pValue->SetInteger(GetInteger(eAttribute));
+}
+
+void CJX_Node::Script_Attribute_IntegerRead(CFXJSE_Value* pValue,
+                                            bool bSetting,
+                                            XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetInteger(GetInteger(eAttribute));
+}
+
+void CJX_Node::Script_Attribute_BOOL(CFXJSE_Value* pValue,
+                                     bool bSetting,
+                                     XFA_Attribute eAttribute) {
+  if (bSetting) {
+    SetBoolean(eAttribute, pValue->ToBoolean(), true);
+    return;
+  }
+  pValue->SetString(GetBoolean(eAttribute) ? "1" : "0");
+}
+
+void CJX_Node::Script_Attribute_BOOLRead(CFXJSE_Value* pValue,
+                                         bool bSetting,
+                                         XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetString(GetBoolean(eAttribute) ? "1" : "0");
+}
+
+void CJX_Node::Script_Attribute_String(CFXJSE_Value* pValue,
+                                       bool bSetting,
+                                       XFA_Attribute eAttribute) {
+  if (!bSetting) {
+    pValue->SetString(GetAttribute(eAttribute).UTF8Encode().AsStringView());
+    return;
+  }
+
+  WideString wsValue = pValue->ToWideString();
+  SetAttribute(eAttribute, wsValue.AsStringView(), true);
+  if (eAttribute != XFA_Attribute::Use ||
+      GetXFANode()->GetElementType() != XFA_Element::Desc) {
+    return;
+  }
+
+  CXFA_Node* pTemplateNode =
+      ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Template));
+  CXFA_Node* pProtoRoot =
+      pTemplateNode->GetFirstChildByClass(XFA_Element::Subform)
+          ->GetFirstChildByClass(XFA_Element::Proto);
+
+  WideString wsID;
+  WideString wsSOM;
+  if (!wsValue.IsEmpty()) {
+    if (wsValue[0] == '#')
+      wsID = WideString(wsValue.c_str() + 1, wsValue.GetLength() - 1);
+    else
+      wsSOM = wsValue;
+  }
+
+  CXFA_Node* pProtoNode = nullptr;
+  if (!wsSOM.IsEmpty()) {
+    XFA_RESOLVENODE_RS resolveNodeRS;
+    bool iRet = GetDocument()->GetScriptContext()->ResolveObjects(
+        pProtoRoot, wsSOM.AsStringView(), &resolveNodeRS,
+        XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
+            XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
+            XFA_RESOLVENODE_Siblings,
+        nullptr);
+    if (iRet && resolveNodeRS.objects.front()->IsNode())
+      pProtoNode = resolveNodeRS.objects.front()->AsNode();
+
+  } else if (!wsID.IsEmpty()) {
+    pProtoNode = GetDocument()->GetNodeByID(pProtoRoot, wsID.AsStringView());
+  }
+  if (!pProtoNode)
+    return;
+
+  CXFA_Node* pHeadChild = GetXFANode()->GetNodeItem(XFA_NODEITEM_FirstChild);
+  while (pHeadChild) {
+    CXFA_Node* pSibling = pHeadChild->GetNodeItem(XFA_NODEITEM_NextSibling);
+    GetXFANode()->RemoveChild(pHeadChild, true);
+    pHeadChild = pSibling;
+  }
+
+  std::unique_ptr<CXFA_Node> pProtoForm(pProtoNode->CloneTemplateToForm(true));
+  pHeadChild = pProtoForm->GetNodeItem(XFA_NODEITEM_FirstChild);
+  while (pHeadChild) {
+    CXFA_Node* pSibling = pHeadChild->GetNodeItem(XFA_NODEITEM_NextSibling);
+    pProtoForm->RemoveChild(pHeadChild, true);
+    GetXFANode()->InsertChild(pHeadChild, nullptr);
+    pHeadChild = pSibling;
+  }
+
+  GetDocument()->RemovePurgeNode(pProtoForm.get());
+}
+
+void CJX_Node::Script_Attribute_StringRead(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetString(GetAttribute(eAttribute).UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_Delta_CurrentValue(CFXJSE_Value* pValue,
+                                         bool bSetting,
+                                         XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_Delta_SavedValue(CFXJSE_Value* pValue,
+                                       bool bSetting,
+                                       XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_Delta_Target(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_Som_Message(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_SOM_MESSAGETYPE iMessageType) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  bool bNew = false;
+  CXFA_ValidateData validateData = pWidgetData->GetValidateData(false);
+  if (!validateData.HasValidNode()) {
+    validateData = pWidgetData->GetValidateData(true);
+    bNew = true;
+  }
+
+  if (bSetting) {
+    switch (iMessageType) {
+      case XFA_SOM_ValidationMessage:
+        validateData.SetScriptMessageText(pValue->ToWideString());
+        break;
+      case XFA_SOM_FormatMessage:
+        validateData.SetFormatMessageText(pValue->ToWideString());
+        break;
+      case XFA_SOM_MandatoryMessage:
+        validateData.SetNullMessageText(pValue->ToWideString());
+        break;
+      default:
+        break;
+    }
+    if (!bNew) {
+      CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+      if (!pNotify) {
+        return;
+      }
+      pNotify->AddCalcValidate(GetXFANode());
+    }
+    return;
+  }
+
+  WideString wsMessage;
+  switch (iMessageType) {
+    case XFA_SOM_ValidationMessage:
+      wsMessage = validateData.GetScriptMessageText();
+      break;
+    case XFA_SOM_FormatMessage:
+      wsMessage = validateData.GetFormatMessageText();
+      break;
+    case XFA_SOM_MandatoryMessage:
+      wsMessage = validateData.GetNullMessageText();
+      break;
+    default:
+      break;
+  }
+  pValue->SetString(wsMessage.UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_Som_ValidationMessage(CFXJSE_Value* pValue,
+                                            bool bSetting,
+                                            XFA_Attribute eAttribute) {
+  Script_Som_Message(pValue, bSetting, XFA_SOM_ValidationMessage);
+}
+
+void CJX_Node::Script_Field_Length(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData) {
+    pValue->SetInteger(0);
+    return;
+  }
+  pValue->SetInteger(pWidgetData->CountChoiceListItems(true));
+}
+
+void CJX_Node::Script_Som_DefaultValue(CFXJSE_Value* pValue,
+                                       bool bSetting,
+                                       XFA_Attribute /* unused */) {
+  XFA_Element eType = GetXFANode()->GetElementType();
+  if (eType == XFA_Element::Field) {
+    Script_Field_DefaultValue(pValue, bSetting, XFA_Attribute::Unknown);
+    return;
+  }
+  if (eType == XFA_Element::Draw) {
+    Script_Draw_DefaultValue(pValue, bSetting, XFA_Attribute::Unknown);
+    return;
+  }
+  if (eType == XFA_Element::Boolean) {
+    Script_Boolean_Value(pValue, bSetting, XFA_Attribute::Unknown);
+    return;
+  }
+
+  if (bSetting) {
+    WideString wsNewValue;
+    if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
+      wsNewValue = pValue->ToWideString();
+
+    WideString wsFormatValue(wsNewValue);
+    CXFA_WidgetData* pContainerWidgetData = nullptr;
+    if (GetXFANode()->GetPacketType() == XFA_PacketType::Datasets) {
+      WideString wsPicture;
+      for (const auto& pFormNode : *(GetXFANode()->GetBindItems())) {
+        if (!pFormNode || pFormNode->HasRemovedChildren())
+          continue;
+
+        pContainerWidgetData = pFormNode->GetContainerWidgetData();
+        if (pContainerWidgetData) {
+          wsPicture = pContainerWidgetData->GetPictureContent(
+              XFA_VALUEPICTURE_DataBind);
+        }
+        if (!wsPicture.IsEmpty())
+          break;
+
+        pContainerWidgetData = nullptr;
+      }
+    } else if (GetXFANode()->GetPacketType() == XFA_PacketType::Form) {
+      pContainerWidgetData = GetXFANode()->GetContainerWidgetData();
+    }
+
+    if (pContainerWidgetData)
+      wsFormatValue = pContainerWidgetData->GetFormatDataValue(wsNewValue);
+
+    SetContent(wsNewValue, wsFormatValue, true, true, true);
+    return;
+  }
+
+  WideString content = GetContent(true);
+  if (content.IsEmpty() && eType != XFA_Element::Text &&
+      eType != XFA_Element::SubmitUrl) {
+    pValue->SetNull();
+  } else if (eType == XFA_Element::Integer) {
+    pValue->SetInteger(FXSYS_wtoi(content.c_str()));
+  } else if (eType == XFA_Element::Float || eType == XFA_Element::Decimal) {
+    CFX_Decimal decimal(content.AsStringView());
+    pValue->SetFloat((float)(double)decimal);
+  } else {
+    pValue->SetString(content.UTF8Encode().AsStringView());
+  }
+}
+
+void CJX_Node::Script_Som_DefaultValue_Read(CFXJSE_Value* pValue,
+                                            bool bSetting,
+                                            XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+
+  WideString content = GetContent(true);
+  if (content.IsEmpty()) {
+    pValue->SetNull();
+    return;
+  }
+  pValue->SetString(content.UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_Boolean_Value(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  if (!bSetting) {
+    WideString wsValue = GetContent(true);
+    pValue->SetBoolean(wsValue == L"1");
+    return;
+  }
+
+  ByteString newValue;
+  if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
+    newValue = pValue->ToString();
+
+  int32_t iValue = FXSYS_atoi(newValue.c_str());
+  WideString wsNewValue(iValue == 0 ? L"0" : L"1");
+  WideString wsFormatValue(wsNewValue);
+  CXFA_WidgetData* pContainerWidgetData =
+      GetXFANode()->GetContainerWidgetData();
+  if (pContainerWidgetData)
+    wsFormatValue = pContainerWidgetData->GetFormatDataValue(wsNewValue);
+
+  SetContent(wsNewValue, wsFormatValue, true, true, true);
+}
+
+void CJX_Node::Script_Som_BorderColor(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  CXFA_BorderData borderData = pWidgetData->GetBorderData(true);
+  int32_t iSize = borderData.CountEdges();
+  if (bSetting) {
+    int32_t r = 0;
+    int32_t g = 0;
+    int32_t b = 0;
+    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    FX_ARGB rgb = ArgbEncode(100, r, g, b);
+    for (int32_t i = 0; i < iSize; ++i)
+      borderData.GetEdgeData(i).SetColor(rgb);
+
+    return;
+  }
+
+  FX_ARGB color = borderData.GetEdgeData(0).GetColor();
+  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).UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_Som_BorderWidth(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  CXFA_BorderData borderData = pWidgetData->GetBorderData(true);
+  if (bSetting) {
+    CXFA_Measurement thickness = borderData.GetEdgeData(0).GetMSThickness();
+    pValue->SetString(thickness.ToString().UTF8Encode().AsStringView());
+    return;
+  }
+
+  WideString wsThickness = pValue->ToWideString();
+  for (int32_t i = 0; i < borderData.CountEdges(); ++i) {
+    borderData.GetEdgeData(i).SetMSThickness(
+        CXFA_Measurement(wsThickness.AsStringView()));
+  }
+}
+
+void CJX_Node::Script_Som_FillColor(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  CXFA_BorderData borderData = pWidgetData->GetBorderData(true);
+  CXFA_FillData borderfillData = borderData.GetFillData(true);
+  CXFA_Node* pNode = borderfillData.GetNode();
+  if (!pNode)
+    return;
+
+  if (bSetting) {
+    int32_t r;
+    int32_t g;
+    int32_t b;
+    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    FX_ARGB color = ArgbEncode(0xff, r, g, b);
+    borderfillData.SetColor(color);
+    return;
+  }
+
+  FX_ARGB color = borderfillData.GetColor(false);
+  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).UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_Som_DataNode(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+
+  CXFA_Node* pDataNode = GetXFANode()->GetBindData();
+  if (!pDataNode) {
+    pValue->SetNull();
+    return;
+  }
+
+  pValue->Assign(
+      GetDocument()->GetScriptContext()->GetJSValueFromMap(pDataNode));
+}
+
+void CJX_Node::Script_Draw_DefaultValue(CFXJSE_Value* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute) {
+  if (!bSetting) {
+    WideString content = GetContent(true);
+    if (content.IsEmpty())
+      pValue->SetNull();
+    else
+      pValue->SetString(content.UTF8Encode().AsStringView());
+
+    return;
+  }
+
+  if (!pValue || !pValue->IsString())
+    return;
+
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  XFA_Element uiType = pWidgetData->GetUIType();
+  if (uiType != XFA_Element::Text)
+    return;
+
+  WideString wsNewValue = pValue->ToWideString();
+  SetContent(wsNewValue, wsNewValue, true, true, true);
+}
+
+void CJX_Node::Script_Field_DefaultValue(CFXJSE_Value* pValue,
+                                         bool bSetting,
+                                         XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  if (bSetting) {
+    if (pValue) {
+      pWidgetData->SetPreNull(pWidgetData->IsNull());
+      pWidgetData->SetIsNull(pValue->IsNull());
+    }
+
+    WideString wsNewText;
+    if (pValue && !(pValue->IsNull() || pValue->IsUndefined()))
+      wsNewText = pValue->ToWideString();
+
+    CXFA_Node* pUIChild = pWidgetData->GetUIChild();
+    if (pUIChild->GetElementType() == XFA_Element::NumericEdit) {
+      wsNewText =
+          pWidgetData->NumericLimit(wsNewText, pWidgetData->GetLeadDigits(),
+                                    pWidgetData->GetFracDigits());
+    }
+
+    CXFA_WidgetData* pContainerWidgetData =
+        GetXFANode()->GetContainerWidgetData();
+    WideString wsFormatText(wsNewText);
+    if (pContainerWidgetData)
+      wsFormatText = pContainerWidgetData->GetFormatDataValue(wsNewText);
+
+    SetContent(wsNewText, wsFormatText, true, true, true);
+    return;
+  }
+
+  WideString content = GetContent(true);
+  if (content.IsEmpty()) {
+    pValue->SetNull();
+    return;
+  }
+
+  CXFA_Node* pUIChild = pWidgetData->GetUIChild();
+  CXFA_Node* pNode = pWidgetData->GetFormValueData().GetNode()->GetNodeItem(
+      XFA_NODEITEM_FirstChild);
+  if (pNode && pNode->GetElementType() == XFA_Element::Decimal) {
+    if (pUIChild->GetElementType() == XFA_Element::NumericEdit &&
+        (pNode->JSNode()->GetInteger(XFA_Attribute::FracDigits) == -1)) {
+      pValue->SetString(content.UTF8Encode().AsStringView());
+    } else {
+      CFX_Decimal decimal(content.AsStringView());
+      pValue->SetFloat((float)(double)decimal);
+    }
+  } else if (pNode && pNode->GetElementType() == XFA_Element::Integer) {
+    pValue->SetInteger(FXSYS_wtoi(content.c_str()));
+  } else if (pNode && pNode->GetElementType() == XFA_Element::Boolean) {
+    pValue->SetBoolean(FXSYS_wtoi(content.c_str()) == 0 ? false : true);
+  } else if (pNode && pNode->GetElementType() == XFA_Element::Float) {
+    CFX_Decimal decimal(content.AsStringView());
+    pValue->SetFloat((float)(double)decimal);
+  } else {
+    pValue->SetString(content.UTF8Encode().AsStringView());
+  }
+}
+
+void CJX_Node::Script_Field_EditValue(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  if (bSetting) {
+    pWidgetData->SetValue(XFA_VALUEPICTURE_Edit, pValue->ToWideString());
+    return;
+  }
+  pValue->SetString(
+      pWidgetData->GetValue(XFA_VALUEPICTURE_Edit).UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_Som_FontColor(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  CXFA_FontData fontData = pWidgetData->GetFontData(true);
+  CXFA_Node* pNode = fontData.GetNode();
+  if (!pNode)
+    return;
+
+  if (bSetting) {
+    int32_t r;
+    int32_t g;
+    int32_t b;
+    std::tie(r, g, b) = StrToRGB(pValue->ToWideString());
+    FX_ARGB color = ArgbEncode(0xff, r, g, b);
+    fontData.SetColor(color);
+    return;
+  }
+
+  int32_t a;
+  int32_t r;
+  int32_t g;
+  int32_t b;
+  std::tie(a, r, g, b) = ArgbDecode(fontData.GetColor());
+  pValue->SetString(ByteString::Format("%d,%d,%d", r, g, b).AsStringView());
+}
+
+void CJX_Node::Script_Field_FormatMessage(CFXJSE_Value* pValue,
+                                          bool bSetting,
+                                          XFA_Attribute eAttribute) {
+  Script_Som_Message(pValue, bSetting, XFA_SOM_FormatMessage);
+}
+
+void CJX_Node::Script_Field_FormattedValue(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  if (bSetting) {
+    pWidgetData->SetValue(XFA_VALUEPICTURE_Display, pValue->ToWideString());
+    return;
+  }
+  pValue->SetString(pWidgetData->GetValue(XFA_VALUEPICTURE_Display)
+                        .UTF8Encode()
+                        .AsStringView());
+}
+
+void CJX_Node::Script_Som_Mandatory(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  CXFA_ValidateData validateData = pWidgetData->GetValidateData(true);
+  if (bSetting) {
+    validateData.SetNullTest(pValue->ToWideString());
+    return;
+  }
+
+  WideString str = CXFA_Node::AttributeEnumToName(validateData.GetNullTest());
+  pValue->SetString(str.UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_Som_MandatoryMessage(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute) {
+  Script_Som_Message(pValue, bSetting, XFA_SOM_MandatoryMessage);
+}
+
+void CJX_Node::Script_Field_ParentSubform(CFXJSE_Value* pValue,
+                                          bool bSetting,
+                                          XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetNull();
+}
+
+void CJX_Node::Script_Field_SelectedIndex(CFXJSE_Value* pValue,
+                                          bool bSetting,
+                                          XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  if (!bSetting) {
+    pValue->SetInteger(pWidgetData->GetSelectedItem(0));
+    return;
+  }
+
+  int32_t iIndex = pValue->ToInteger();
+  if (iIndex == -1) {
+    pWidgetData->ClearAllSelections();
+    return;
+  }
+
+  pWidgetData->SetItemState(iIndex, true, true, true, true);
+}
+
+void CJX_Node::Script_ExclGroup_ErrorText(CFXJSE_Value* pValue,
+                                          bool bSetting,
+                                          XFA_Attribute eAttribute) {
+  if (bSetting)
+    ThrowInvalidPropertyException();
+}
+
+void CJX_Node::Script_ExclGroup_DefaultAndRawValue(CFXJSE_Value* pValue,
+                                                   bool bSetting,
+                                                   XFA_Attribute eAttribute) {
+  CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+  if (!pWidgetData)
+    return;
+
+  if (bSetting) {
+    pWidgetData->SetSelectedMemberByValue(pValue->ToWideString().AsStringView(),
+                                          true, true, true);
+    return;
+  }
+
+  WideString wsValue = GetContent(true);
+  XFA_VERSION curVersion = GetDocument()->GetCurVersionMode();
+  if (wsValue.IsEmpty() && curVersion >= XFA_VERSION_300) {
+    pValue->SetNull();
+    return;
+  }
+  pValue->SetString(wsValue.UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_ExclGroup_Transient(CFXJSE_Value* pValue,
+                                          bool bSetting,
+                                          XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_Som_InstanceIndex(CFXJSE_Value* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute) {
+  if (!bSetting) {
+    pValue->SetInteger(Subform_and_SubformSet_InstanceIndex());
+    return;
+  }
+
+  int32_t iTo = pValue->ToInteger();
+  int32_t iFrom = Subform_and_SubformSet_InstanceIndex();
+  CXFA_Node* pManagerNode = nullptr;
+  for (CXFA_Node* pNode = GetXFANode()->GetNodeItem(XFA_NODEITEM_PrevSibling);
+       pNode; pNode = pNode->GetNodeItem(XFA_NODEITEM_PrevSibling)) {
+    if (pNode->GetElementType() == XFA_Element::InstanceManager) {
+      pManagerNode = pNode;
+      break;
+    }
+  }
+  if (!pManagerNode)
+    return;
+
+  pManagerNode->JSNode()->InstanceManager_MoveInstance(iTo, iFrom);
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return;
+
+  CXFA_Node* pToInstance = pManagerNode->GetItem(iTo);
+  if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform) {
+    pNotify->RunSubformIndexChange(pToInstance);
+  }
+
+  CXFA_Node* pFromInstance = pManagerNode->GetItem(iFrom);
+  if (pFromInstance &&
+      pFromInstance->GetElementType() == XFA_Element::Subform) {
+    pNotify->RunSubformIndexChange(pFromInstance);
+  }
+}
+
+void CJX_Node::Script_Subform_InstanceManager(CFXJSE_Value* pValue,
+                                              bool bSetting,
+                                              XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+
+  WideString wsName = GetCData(XFA_Attribute::Name);
+  CXFA_Node* pInstanceMgr = nullptr;
+  for (CXFA_Node* pNode = GetXFANode()->GetNodeItem(XFA_NODEITEM_PrevSibling);
+       pNode; pNode = pNode->GetNodeItem(XFA_NODEITEM_PrevSibling)) {
+    if (pNode->GetElementType() == XFA_Element::InstanceManager) {
+      WideString wsInstMgrName = pNode->JSNode()->GetCData(XFA_Attribute::Name);
+      if (wsInstMgrName.GetLength() >= 1 && wsInstMgrName[0] == '_' &&
+          wsInstMgrName.Right(wsInstMgrName.GetLength() - 1) == wsName) {
+        pInstanceMgr = pNode;
+      }
+      break;
+    }
+  }
+  if (!pInstanceMgr) {
+    pValue->SetNull();
+    return;
+  }
+
+  pValue->Assign(
+      GetDocument()->GetScriptContext()->GetJSValueFromMap(pInstanceMgr));
+}
+
+void CJX_Node::Script_Subform_Locale(CFXJSE_Value* pValue,
+                                     bool bSetting,
+                                     XFA_Attribute eAttribute) {
+  if (bSetting) {
+    SetCData(XFA_Attribute::Locale, pValue->ToWideString(), true, true);
+    return;
+  }
+
+  WideString wsLocaleName;
+  GetXFANode()->GetLocaleName(wsLocaleName);
+  pValue->SetString(wsLocaleName.UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_InstanceManager_Max(CFXJSE_Value* pValue,
+                                          bool bSetting,
+                                          XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetInteger(CXFA_OccurData(GetXFANode()->GetOccurNode()).GetMax());
+}
+
+void CJX_Node::Script_InstanceManager_Min(CFXJSE_Value* pValue,
+                                          bool bSetting,
+                                          XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetInteger(CXFA_OccurData(GetXFANode()->GetOccurNode()).GetMin());
+}
+
+void CJX_Node::Script_InstanceManager_Count(CFXJSE_Value* pValue,
+                                            bool bSetting,
+                                            XFA_Attribute eAttribute) {
+  if (bSetting) {
+    pValue->SetInteger(GetXFANode()->GetCount());
+    return;
+  }
+  InstanceManager_SetInstances(pValue->ToInteger());
+}
+
+void CJX_Node::Script_Occur_Max(CFXJSE_Value* pValue,
+                                bool bSetting,
+                                XFA_Attribute eAttribute) {
+  CXFA_OccurData occurData(GetXFANode());
+  if (!bSetting) {
+    pValue->SetInteger(occurData.GetMax());
+    return;
+  }
+  occurData.SetMax(pValue->ToInteger());
+}
+
+void CJX_Node::Script_Occur_Min(CFXJSE_Value* pValue,
+                                bool bSetting,
+                                XFA_Attribute eAttribute) {
+  CXFA_OccurData occurData(GetXFANode());
+  if (!bSetting) {
+    pValue->SetInteger(occurData.GetMin());
+    return;
+  }
+  occurData.SetMin(pValue->ToInteger());
+}
+
+void CJX_Node::Script_Form_Checksum(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute) {
+  if (bSetting) {
+    SetAttribute(XFA_Attribute::Checksum, pValue->ToWideString().AsStringView(),
+                 false);
+    return;
+  }
+
+  pdfium::Optional<WideString> checksum =
+      TryAttribute(XFA_Attribute::Checksum, false);
+  pValue->SetString(checksum ? checksum->UTF8Encode().AsStringView() : "");
+}
+
+void CJX_Node::Script_Packet_Content(CFXJSE_Value* pValue,
+                                     bool bSetting,
+                                     XFA_Attribute eAttribute) {
+  CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
+  if (bSetting) {
+    if (pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element) {
+      CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
+      pXMLElement->SetTextData(pValue->ToWideString());
+    }
+    return;
+  }
+
+  WideString wsTextData;
+  if (pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element) {
+    CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
+    wsTextData = pXMLElement->GetTextData();
+  }
+
+  pValue->SetString(wsTextData.UTF8Encode().AsStringView());
+}
+
+void CJX_Node::Script_Source_Db(CFXJSE_Value* pValue,
+                                bool bSetting,
+                                XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_Xfa_This(CFXJSE_Value* pValue,
+                               bool bSetting,
+                               XFA_Attribute eAttribute) {
+  if (bSetting)
+    return;
+
+  CXFA_Object* pThis = GetDocument()->GetScriptContext()->GetThisObject();
+  ASSERT(pThis);
+  pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(pThis));
+}
+
+void CJX_Node::Script_Handler_Version(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_SubmitFormat_Mode(CFXJSE_Value* pValue,
+                                        bool bSetting,
+                                        XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_Extras_Type(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute) {}
+
+void CJX_Node::Script_Script_Stateless(CFXJSE_Value* pValue,
+                                       bool bSetting,
+                                       XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetString(FX_UTF8Encode(WideStringView(L"0", 1)).AsStringView());
+}
+
+void CJX_Node::Script_Encrypt_Format(CFXJSE_Value* pValue,
+                                     bool bSetting,
+                                     XFA_Attribute eAttribute) {}
+
+void CJX_Node::SetWidgetData(std::unique_ptr<CXFA_WidgetData> data) {
+  widget_data_ = std::move(data);
+}
+
+pdfium::Optional<WideString> CJX_Node::TryNamespace() {
+  if (GetXFANode()->IsModelNode() ||
+      GetXFANode()->GetElementType() == XFA_Element::Packet) {
+    CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
+    if (!pXMLNode || pXMLNode->GetType() != FX_XMLNODE_Element)
+      return {};
+
+    return {static_cast<CFX_XMLElement*>(pXMLNode)->GetNamespaceURI()};
+  }
+
+  if (GetXFANode()->GetPacketType() != XFA_PacketType::Datasets)
+    return GetXFANode()->GetModelNode()->JSNode()->TryNamespace();
+
+  CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode();
+  if (!pXMLNode || pXMLNode->GetType() != FX_XMLNODE_Element)
+    return {};
+
+  if (GetXFANode()->GetElementType() == XFA_Element::DataValue &&
+      GetEnum(XFA_Attribute::Contains) == XFA_AttributeEnum::MetaData) {
+    WideString wsNamespace;
+    bool ret = XFA_FDEExtension_ResolveNamespaceQualifier(
+        static_cast<CFX_XMLElement*>(pXMLNode),
+        GetCData(XFA_Attribute::QualifiedName), &wsNamespace);
+    if (!ret)
+      return {};
+    return {wsNamespace};
+  }
+  return {static_cast<CFX_XMLElement*>(pXMLNode)->GetNamespaceURI()};
+}
+
+CXFA_Node* CJX_Node::GetProperty(int32_t index,
+                                 XFA_Element eProperty,
+                                 bool bCreateProperty) {
+  if (index < 0 || index >= GetXFANode()->PropertyOccuranceCount(eProperty))
+    return nullptr;
+
+  int32_t iCount = 0;
+  for (CXFA_Node* pNode = GetXFANode()->GetChildNode(); pNode;
+       pNode = pNode->GetNodeItem(XFA_NODEITEM_NextSibling)) {
+    if (pNode->GetElementType() == eProperty) {
+      iCount++;
+      if (iCount > index)
+        return pNode;
+    }
+  }
+  if (!bCreateProperty)
+    return nullptr;
+
+  if (GetXFANode()->HasPropertyFlags(eProperty, XFA_PROPERTYFLAG_OneOf)) {
+    for (CXFA_Node* pNode = GetXFANode()->GetChildNode(); pNode;
+         pNode = pNode->GetNodeItem(XFA_NODEITEM_NextSibling)) {
+      if (GetXFANode()->HasPropertyFlags(pNode->GetElementType(),
+                                         XFA_PROPERTYFLAG_OneOf)) {
+        return nullptr;
+      }
+    }
+  }
+
+  CXFA_Node* pNewNode = nullptr;
+  for (; iCount <= index; ++iCount) {
+    pNewNode =
+        GetDocument()->CreateNode(GetXFANode()->GetPacketType(), eProperty);
+    if (!pNewNode)
+      return nullptr;
+    GetXFANode()->InsertChild(pNewNode, nullptr);
+    pNewNode->SetFlag(XFA_NodeFlag_Initialized, true);
+  }
+  return pNewNode;
+}
+
+int32_t CJX_Node::execSingleEventByName(const WideStringView& wsEventName,
+                                        XFA_Element eType) {
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (!pNotify)
+    return XFA_EVENTERROR_NotExist;
+
+  const XFA_ExecEventParaInfo* eventParaInfo =
+      GetEventParaInfoByName(wsEventName);
+  if (!eventParaInfo)
+    return XFA_EVENTERROR_NotExist;
+
+  switch (eventParaInfo->m_validFlags) {
+    case EventAppliesToo::kNone:
+      return XFA_EVENTERROR_NotExist;
+    case EventAppliesToo::kAll:
+    case EventAppliesToo::kAllNonRecursive:
+      return pNotify->ExecEventByDeepFirst(
+          GetXFANode(), eventParaInfo->m_eventType, false,
+          eventParaInfo->m_validFlags == EventAppliesToo::kAll);
+    case EventAppliesToo::kSubform:
+      if (eType != XFA_Element::Subform)
+        return XFA_EVENTERROR_NotExist;
+
+      return pNotify->ExecEventByDeepFirst(
+          GetXFANode(), eventParaInfo->m_eventType, false, false);
+    case EventAppliesToo::kFieldOrExclusion: {
+      if (eType != XFA_Element::ExclGroup && eType != XFA_Element::Field)
+        return XFA_EVENTERROR_NotExist;
+
+      CXFA_Node* pParentNode = GetXFANode()->GetNodeItem(XFA_NODEITEM_Parent);
+      if (pParentNode &&
+          pParentNode->GetElementType() == XFA_Element::ExclGroup) {
+        // TODO(dsinclair): This seems like a bug, we do the same work twice?
+        pNotify->ExecEventByDeepFirst(GetXFANode(), eventParaInfo->m_eventType,
+                                      false, false);
+      }
+      return pNotify->ExecEventByDeepFirst(
+          GetXFANode(), eventParaInfo->m_eventType, false, false);
+    }
+    case EventAppliesToo::kField:
+      if (eType != XFA_Element::Field)
+        return XFA_EVENTERROR_NotExist;
+
+      return pNotify->ExecEventByDeepFirst(
+          GetXFANode(), eventParaInfo->m_eventType, false, false);
+    case EventAppliesToo::kSignature: {
+      CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+      if (!pWidgetData)
+        return XFA_EVENTERROR_NotExist;
+
+      CXFA_Node* pUINode = pWidgetData->GetUIChild();
+      if (pUINode->GetElementType() != XFA_Element::Signature)
+        return XFA_EVENTERROR_NotExist;
+
+      return pNotify->ExecEventByDeepFirst(
+          GetXFANode(), eventParaInfo->m_eventType, false, false);
+    }
+    case EventAppliesToo::kChoiceList: {
+      CXFA_WidgetData* pWidgetData = GetXFANode()->GetWidgetData();
+      if (!pWidgetData)
+        return XFA_EVENTERROR_NotExist;
+
+      CXFA_Node* pUINode = pWidgetData->GetUIChild();
+      if (pUINode->GetElementType() != XFA_Element::ChoiceList ||
+          pWidgetData->IsListBox()) {
+        return XFA_EVENTERROR_NotExist;
+      }
+      return pNotify->ExecEventByDeepFirst(
+          GetXFANode(), eventParaInfo->m_eventType, false, false);
+    }
+  }
+  return XFA_EVENTERROR_NotExist;
+}
+
+void CJX_Node::ThrowMissingPropertyException(const WideString& obj,
+                                             const WideString& prop) const {
+  ThrowException(L"'%ls' doesn't have property '%ls'.", obj.c_str(),
+                 prop.c_str());
+}
+
+void CJX_Node::ThrowTooManyOccurancesException(const WideString& obj) const {
+  ThrowException(
+      L"The element [%ls] has violated its allowable number of occurrences.",
+      obj.c_str());
+}
+
+bool CJX_Node::SetContent(const WideString& wsContent,
+                          const WideString& wsXMLValue,
+                          bool bNotify,
+                          bool bScriptModify,
+                          bool bSyncData) {
+  CXFA_Node* pNode = nullptr;
+  CXFA_Node* pBindNode = nullptr;
+  switch (GetXFANode()->GetObjectType()) {
+    case XFA_ObjectType::ContainerNode: {
+      if (XFA_FieldIsMultiListBox(GetXFANode())) {
+        CXFA_Node* pValue = GetProperty(0, XFA_Element::Value, true);
+        if (!pValue)
+          break;
+
+        CXFA_Node* pChildValue = pValue->GetNodeItem(XFA_NODEITEM_FirstChild);
+        ASSERT(pChildValue);
+        pChildValue->JSNode()->SetCData(XFA_Attribute::ContentType, L"text/xml",
+                                        false, false);
+        pChildValue->JSNode()->SetContent(wsContent, wsContent, bNotify,
+                                          bScriptModify, false);
+        CXFA_Node* pBind = GetXFANode()->GetBindData();
+        if (bSyncData && pBind) {
+          std::vector<WideString> wsSaveTextArray;
+          size_t iSize = 0;
+          if (!wsContent.IsEmpty()) {
+            size_t iStart = 0;
+            size_t iLength = wsContent.GetLength();
+            auto iEnd = wsContent.Find(L'\n', iStart);
+            iEnd = !iEnd.has_value() ? iLength : iEnd;
+            while (iEnd.value() >= iStart) {
+              wsSaveTextArray.push_back(
+                  wsContent.Mid(iStart, iEnd.value() - iStart));
+              iStart = iEnd.value() + 1;
+              if (iStart >= iLength)
+                break;
+
+              iEnd = wsContent.Find(L'\n', iStart);
+              if (!iEnd.has_value()) {
+                wsSaveTextArray.push_back(
+                    wsContent.Mid(iStart, iLength - iStart));
+              }
+            }
+            iSize = wsSaveTextArray.size();
+          }
+          if (iSize == 0) {
+            while (CXFA_Node* pChildNode =
+                       pBind->GetNodeItem(XFA_NODEITEM_FirstChild)) {
+              pBind->RemoveChild(pChildNode, true);
+            }
+          } else {
+            std::vector<CXFA_Node*> valueNodes = pBind->GetNodeList(
+                XFA_NODEFILTER_Children, XFA_Element::DataValue);
+            size_t iDatas = valueNodes.size();
+            if (iDatas < iSize) {
+              size_t iAddNodes = iSize - iDatas;
+              CXFA_Node* pValueNodes = nullptr;
+              while (iAddNodes-- > 0) {
+                pValueNodes =
+                    pBind->CreateSamePacketNode(XFA_Element::DataValue);
+                pValueNodes->JSNode()->SetCData(XFA_Attribute::Name, L"value",
+                                                false, false);
+                pValueNodes->CreateXMLMappingNode();
+                pBind->InsertChild(pValueNodes, nullptr);
+              }
+              pValueNodes = nullptr;
+            } else if (iDatas > iSize) {
+              size_t iDelNodes = iDatas - iSize;
+              while (iDelNodes-- > 0) {
+                pBind->RemoveChild(pBind->GetNodeItem(XFA_NODEITEM_FirstChild),
+                                   true);
+              }
+            }
+            int32_t i = 0;
+            for (CXFA_Node* pValueNode =
+                     pBind->GetNodeItem(XFA_NODEITEM_FirstChild);
+                 pValueNode; pValueNode = pValueNode->GetNodeItem(
+                                 XFA_NODEITEM_NextSibling)) {
+              pValueNode->JSNode()->SetAttributeValue(
+                  wsSaveTextArray[i], wsSaveTextArray[i], false, false);
+              i++;
+            }
+          }
+          for (const auto& pArrayNode : *(pBind->GetBindItems())) {
+            if (pArrayNode.Get() != GetXFANode()) {
+              pArrayNode->JSNode()->SetContent(wsContent, wsContent, bNotify,
+                                               bScriptModify, false);
+            }
+          }
+        }
+        break;
+      }
+      if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) {
+        pNode = GetXFANode();
+      } else {
+        CXFA_Node* pValue = GetProperty(0, XFA_Element::Value, true);
+        if (!pValue)
+          break;
+
+        CXFA_Node* pChildValue = pValue->GetNodeItem(XFA_NODEITEM_FirstChild);
+        ASSERT(pChildValue);
+        pChildValue->JSNode()->SetContent(wsContent, wsContent, bNotify,
+                                          bScriptModify, false);
+      }
+      pBindNode = GetXFANode()->GetBindData();
+      if (pBindNode && bSyncData) {
+        pBindNode->JSNode()->SetContent(wsContent, wsXMLValue, bNotify,
+                                        bScriptModify, false);
+        for (const auto& pArrayNode : *(pBindNode->GetBindItems())) {
+          if (pArrayNode.Get() != GetXFANode()) {
+            pArrayNode->JSNode()->SetContent(wsContent, wsContent, bNotify,
+                                             true, false);
+          }
+        }
+      }
+      pBindNode = nullptr;
+      break;
+    }
+    case XFA_ObjectType::ContentNode: {
+      WideString wsContentType;
+      if (GetXFANode()->GetElementType() == XFA_Element::ExData) {
+        pdfium::Optional<WideString> ret =
+            TryAttribute(XFA_Attribute::ContentType, false);
+        if (ret)
+          wsContentType = *ret;
+        if (wsContentType == L"text/html") {
+          wsContentType = L"";
+          SetAttribute(XFA_Attribute::ContentType, wsContentType.AsStringView(),
+                       false);
+        }
+      }
+
+      CXFA_Node* pContentRawDataNode =
+          GetXFANode()->GetNodeItem(XFA_NODEITEM_FirstChild);
+      if (!pContentRawDataNode) {
+        pContentRawDataNode = GetXFANode()->CreateSamePacketNode(
+            (wsContentType == L"text/xml") ? XFA_Element::Sharpxml
+                                           : XFA_Element::Sharptext);
+        GetXFANode()->InsertChild(pContentRawDataNode, nullptr);
+      }
+      return pContentRawDataNode->JSNode()->SetContent(
+          wsContent, wsXMLValue, bNotify, bScriptModify, bSyncData);
+    }
+    case XFA_ObjectType::NodeC:
+    case XFA_ObjectType::TextNode:
+      pNode = GetXFANode();
+      break;
+    case XFA_ObjectType::NodeV:
+      pNode = GetXFANode();
+      if (bSyncData && GetXFANode()->GetPacketType() == XFA_PacketType::Form) {
+        CXFA_Node* pParent = GetXFANode()->GetNodeItem(XFA_NODEITEM_Parent);
+        if (pParent) {
+          pParent = pParent->GetNodeItem(XFA_NODEITEM_Parent);
+        }
+        if (pParent && pParent->GetElementType() == XFA_Element::Value) {
+          pParent = pParent->GetNodeItem(XFA_NODEITEM_Parent);
+          if (pParent && pParent->IsContainerNode()) {
+            pBindNode = pParent->GetBindData();
+            if (pBindNode) {
+              pBindNode->JSNode()->SetContent(wsContent, wsXMLValue, bNotify,
+                                              bScriptModify, false);
+            }
+          }
+        }
+      }
+      break;
+    default:
+      if (GetXFANode()->GetElementType() == XFA_Element::DataValue) {
+        pNode = GetXFANode();
+        pBindNode = GetXFANode();
+      }
+      break;
+  }
+  if (!pNode)
+    return false;
+
+  SetAttributeValue(wsContent, wsXMLValue, bNotify, bScriptModify);
+  if (pBindNode && bSyncData) {
+    for (const auto& pArrayNode : *(pBindNode->GetBindItems())) {
+      pArrayNode->JSNode()->SetContent(wsContent, wsContent, bNotify,
+                                       bScriptModify, false);
+    }
+  }
+  return true;
+}
+
+WideString CJX_Node::GetContent(bool bScriptModify) {
+  return TryContent(bScriptModify, true).value_or(WideString());
+}
+
+pdfium::Optional<WideString> CJX_Node::TryContent(bool bScriptModify,
+                                                  bool bProto) {
+  CXFA_Node* pNode = nullptr;
+  switch (GetXFANode()->GetObjectType()) {
+    case XFA_ObjectType::ContainerNode:
+      if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) {
+        pNode = GetXFANode();
+      } else {
+        CXFA_Node* pValue =
+            GetXFANode()->GetChild(0, XFA_Element::Value, false);
+        if (!pValue)
+          return {};
+
+        CXFA_Node* pChildValue = pValue->GetNodeItem(XFA_NODEITEM_FirstChild);
+        if (pChildValue && XFA_FieldIsMultiListBox(GetXFANode())) {
+          pChildValue->JSNode()->SetAttribute(XFA_Attribute::ContentType,
+                                              L"text/xml", false);
+        }
+        if (pChildValue)
+          return pChildValue->JSNode()->TryContent(bScriptModify, bProto);
+        return {};
+      }
+      break;
+    case XFA_ObjectType::ContentNode: {
+      CXFA_Node* pContentRawDataNode =
+          GetXFANode()->GetNodeItem(XFA_NODEITEM_FirstChild);
+      if (!pContentRawDataNode) {
+        XFA_Element element = XFA_Element::Sharptext;
+        if (GetXFANode()->GetElementType() == XFA_Element::ExData) {
+          pdfium::Optional<WideString> contentType =
+              TryAttribute(XFA_Attribute::ContentType, false);
+          if (contentType) {
+            if (*contentType == L"text/html")
+              element = XFA_Element::SharpxHTML;
+            else if (*contentType == L"text/xml")
+              element = XFA_Element::Sharpxml;
+          }
+        }
+        pContentRawDataNode = GetXFANode()->CreateSamePacketNode(element);
+        GetXFANode()->InsertChild(pContentRawDataNode, nullptr);
+      }
+      return pContentRawDataNode->JSNode()->TryContent(bScriptModify, true);
+    }
+    case XFA_ObjectType::NodeC:
+    case XFA_ObjectType::NodeV:
+    case XFA_ObjectType::TextNode:
+      pNode = GetXFANode();
+    default:
+      if (GetXFANode()->GetElementType() == XFA_Element::DataValue)
+        pNode = GetXFANode();
+      break;
+  }
+  if (pNode) {
+    if (bScriptModify) {
+      CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
+      if (pScriptContext)
+        GetDocument()->GetScriptContext()->AddNodesOfRunScript(GetXFANode());
+    }
+    return TryCData(XFA_Attribute::Value, false);
+  }
+  return {};
+}
diff --git a/fxjs/xfa/cjx_node.h b/fxjs/xfa/cjx_node.h
new file mode 100644
index 0000000..a18166f
--- /dev/null
+++ b/fxjs/xfa/cjx_node.h
@@ -0,0 +1,266 @@
+// Copyright 2017 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_CJX_NODE_H_
+#define FXJS_XFA_CJX_NODE_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "fxjs/CJX_Define.h"
+#include "fxjs/xfa/cjx_object.h"
+#include "fxjs/xfa/cjx_tree.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+enum XFA_SOM_MESSAGETYPE {
+  XFA_SOM_ValidationMessage,
+  XFA_SOM_FormatMessage,
+  XFA_SOM_MandatoryMessage
+};
+
+class CXFA_LayoutItem;
+class CXFA_Node;
+class CXFA_WidgetData;
+
+class CJX_Node : public CJX_Tree {
+ public:
+  explicit CJX_Node(CXFA_Node* node);
+  ~CJX_Node() override;
+
+  CXFA_Node* GetXFANode();
+  const CXFA_Node* GetXFANode() const;
+
+  CXFA_Node* GetProperty(int32_t index,
+                         XFA_Element eType,
+                         bool bCreateProperty);
+
+  pdfium::Optional<WideString> TryContent(bool bScriptModify, bool bProto);
+  bool SetContent(const WideString& wsContent,
+                  const WideString& wsXMLValue,
+                  bool bNotify,
+                  bool bScriptModify,
+                  bool bSyncData);
+  WideString GetContent(bool bScriptModify);
+
+  void SetWidgetData(std::unique_ptr<CXFA_WidgetData> data);
+  CXFA_WidgetData* GetWidgetData() const { return widget_data_.get(); }
+
+  void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_ = item; }
+  CXFA_LayoutItem* GetLayoutItem() const { return layout_item_.Get(); }
+
+  void SetCalcRecursionCount(size_t count) { calc_recursion_count_ = count; }
+  size_t GetCalcRecursionCount() const { return calc_recursion_count_; }
+
+  pdfium::Optional<WideString> TryNamespace();
+
+  void ThrowMissingPropertyException(const WideString& obj,
+                                     const WideString& prop) const;
+  void ThrowTooManyOccurancesException(const WideString& obj) const;
+
+  int32_t Subform_and_SubformSet_InstanceIndex();
+  int32_t InstanceManager_SetInstances(int32_t iDesired);
+  int32_t InstanceManager_MoveInstance(int32_t iTo, int32_t iFrom);
+
+  JS_METHOD(applyXSL, CJX_Node);
+  JS_METHOD(assignNode, CJX_Node);
+  JS_METHOD(clone, CJX_Node);
+  JS_METHOD(getAttribute, CJX_Node);
+  JS_METHOD(getElement, CJX_Node);
+  JS_METHOD(isPropertySpecified, CJX_Node);
+  JS_METHOD(loadXML, CJX_Node);
+  JS_METHOD(saveFilteredXML, CJX_Node);
+  JS_METHOD(saveXML, CJX_Node);
+  JS_METHOD(setAttribute, CJX_Node);
+  JS_METHOD(setElement, CJX_Node);
+
+  void Script_NodeClass_Ns(CFXJSE_Value* pValue,
+                           bool bSetting,
+                           XFA_Attribute eAttribute);
+  void Script_NodeClass_Model(CFXJSE_Value* pValue,
+                              bool bSetting,
+                              XFA_Attribute eAttribute);
+  void Script_NodeClass_IsContainer(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute);
+  void Script_NodeClass_IsNull(CFXJSE_Value* pValue,
+                               bool bSetting,
+                               XFA_Attribute eAttribute);
+  void Script_NodeClass_OneOfChild(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute);
+  void Script_ModelClass_Context(CFXJSE_Value* pValue,
+                                 bool bSetting,
+                                 XFA_Attribute eAttribute);
+  void Script_ModelClass_AliasNode(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute);
+  void Script_Delta_CurrentValue(CFXJSE_Value* pValue,
+                                 bool bSetting,
+                                 XFA_Attribute eAttribute);
+  void Script_Delta_SavedValue(CFXJSE_Value* pValue,
+                               bool bSetting,
+                               XFA_Attribute eAttribute);
+  void Script_Delta_Target(CFXJSE_Value* pValue,
+                           bool bSetting,
+                           XFA_Attribute eAttribute);
+  void Script_Attribute_Integer(CFXJSE_Value* pValue,
+                                bool bSetting,
+                                XFA_Attribute eAttribute);
+  void Script_Attribute_IntegerRead(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute);
+  void Script_Attribute_BOOL(CFXJSE_Value* pValue,
+                             bool bSetting,
+                             XFA_Attribute eAttribute);
+  void Script_Attribute_BOOLRead(CFXJSE_Value* pValue,
+                                 bool bSetting,
+                                 XFA_Attribute eAttribute);
+  void Script_Attribute_String(CFXJSE_Value* pValue,
+                               bool bSetting,
+                               XFA_Attribute eAttribute);
+  void Script_Attribute_StringRead(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute);
+  void Script_Som_ValidationMessage(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute);
+  void Script_Field_Length(CFXJSE_Value* pValue,
+                           bool bSetting,
+                           XFA_Attribute eAttribute);
+  void Script_Som_DefaultValue(CFXJSE_Value* pValue,
+                               bool bSetting,
+                               XFA_Attribute eAttribute);
+  void Script_Som_DefaultValue_Read(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute);
+  void Script_Boolean_Value(CFXJSE_Value* pValue,
+                            bool bSetting,
+                            XFA_Attribute eAttribute);
+  void Script_Som_Message(CFXJSE_Value* pValue,
+                          bool bSetting,
+                          XFA_SOM_MESSAGETYPE iMessageType);
+  void Script_Som_BorderColor(CFXJSE_Value* pValue,
+                              bool bSetting,
+                              XFA_Attribute eAttribute);
+  void Script_Som_BorderWidth(CFXJSE_Value* pValue,
+                              bool bSetting,
+                              XFA_Attribute eAttribute);
+  void Script_Som_FillColor(CFXJSE_Value* pValue,
+                            bool bSetting,
+                            XFA_Attribute eAttribute);
+  void Script_Som_DataNode(CFXJSE_Value* pValue,
+                           bool bSetting,
+                           XFA_Attribute eAttribute);
+  void Script_Som_FontColor(CFXJSE_Value* pValue,
+                            bool bSetting,
+                            XFA_Attribute eAttribute);
+  void Script_Som_Mandatory(CFXJSE_Value* pValue,
+                            bool bSetting,
+                            XFA_Attribute eAttribute);
+  void Script_Som_MandatoryMessage(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute);
+  void Script_Som_InstanceIndex(CFXJSE_Value* pValue,
+                                bool bSetting,
+                                XFA_Attribute eAttribute);
+  void Script_Draw_DefaultValue(CFXJSE_Value* pValue,
+                                bool bSetting,
+                                XFA_Attribute eAttribute);
+  void Script_Field_DefaultValue(CFXJSE_Value* pValue,
+                                 bool bSetting,
+                                 XFA_Attribute eAttribute);
+  void Script_Field_EditValue(CFXJSE_Value* pValue,
+                              bool bSetting,
+                              XFA_Attribute eAttribute);
+  void Script_Field_FormatMessage(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute);
+  void Script_Field_FormattedValue(CFXJSE_Value* pValue,
+                                   bool bSetting,
+                                   XFA_Attribute eAttribute);
+  void Script_Field_ParentSubform(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute);
+  void Script_Field_SelectedIndex(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute);
+  void Script_ExclGroup_DefaultAndRawValue(CFXJSE_Value* pValue,
+                                           bool bSetting,
+                                           XFA_Attribute eAttribute);
+  void Script_ExclGroup_ErrorText(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute);
+  void Script_ExclGroup_Transient(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute);
+
+  void Script_Subform_InstanceManager(CFXJSE_Value* pValue,
+                                      bool bSetting,
+                                      XFA_Attribute eAttribute);
+  void Script_Subform_Locale(CFXJSE_Value* pValue,
+                             bool bSetting,
+                             XFA_Attribute eAttribute);
+  void Script_InstanceManager_Count(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute);
+  void Script_InstanceManager_Max(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute);
+  void Script_InstanceManager_Min(CFXJSE_Value* pValue,
+                                  bool bSetting,
+                                  XFA_Attribute eAttribute);
+
+  void Script_Occur_Max(CFXJSE_Value* pValue,
+                        bool bSetting,
+                        XFA_Attribute eAttribute);
+  void Script_Occur_Min(CFXJSE_Value* pValue,
+                        bool bSetting,
+                        XFA_Attribute eAttribute);
+
+  void Script_Form_Checksum(CFXJSE_Value* pValue,
+                            bool bSetting,
+                            XFA_Attribute eAttribute);
+
+  void Script_Packet_Content(CFXJSE_Value* pValue,
+                             bool bSetting,
+                             XFA_Attribute eAttribute);
+
+  void Script_Source_Db(CFXJSE_Value* pValue,
+                        bool bSetting,
+                        XFA_Attribute eAttribute);
+  void Script_Xfa_This(CFXJSE_Value* pValue,
+                       bool bSetting,
+                       XFA_Attribute eAttribute);
+  void Script_Handler_Version(CFXJSE_Value* pValue,
+                              bool bSetting,
+                              XFA_Attribute eAttribute);
+  void Script_SubmitFormat_Mode(CFXJSE_Value* pValue,
+                                bool bSetting,
+                                XFA_Attribute eAttribute);
+  void Script_Extras_Type(CFXJSE_Value* pValue,
+                          bool bSetting,
+                          XFA_Attribute eAttribute);
+  void Script_Encrypt_Format(CFXJSE_Value* pValue,
+                             bool bSetting,
+                             XFA_Attribute eAttribute);
+  void Script_Script_Stateless(CFXJSE_Value* pValue,
+                               bool bSetting,
+                               XFA_Attribute eAttribute);
+
+ protected:
+  int32_t execSingleEventByName(const WideStringView& wsEventName,
+                                XFA_Element eType);
+
+ private:
+  std::unique_ptr<CXFA_WidgetData> widget_data_;
+  UnownedPtr<CXFA_LayoutItem> layout_item_;
+  size_t calc_recursion_count_ = 0;
+  static const CJX_MethodSpec MethodSpecs[];
+};
+
+#endif  // FXJS_XFA_CJX_NODE_H_
diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
new file mode 100644
index 0000000..81ba1ac
--- /dev/null
+++ b/fxjs/xfa/cjx_object.cpp
@@ -0,0 +1,806 @@
+// Copyright 2017 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/cjx_object.h"
+
+#include <utility>
+
+#include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/xml/cfx_xmltext.h"
+#include "fxjs/cfxjse_engine.h"
+#include "fxjs/cfxjse_value.h"
+#include "fxjs/cjs_return.h"
+#include "third_party/base/ptr_util.h"
+#include "xfa/fxfa/cxfa_ffnotify.h"
+#include "xfa/fxfa/cxfa_ffwidget.h"
+#include "xfa/fxfa/parser/cxfa_document.h"
+#include "xfa/fxfa/parser/cxfa_measurement.h"
+#include "xfa/fxfa/parser/cxfa_node.h"
+#include "xfa/fxfa/parser/cxfa_object.h"
+
+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));
+}
+
+XFA_MAPDATABLOCKCALLBACKINFO deleteWideStringCallBack = {XFA_DeleteWideString,
+                                                         XFA_CopyWideString};
+
+enum XFA_KEYTYPE {
+  XFA_KEYTYPE_Custom,
+  XFA_KEYTYPE_Element,
+};
+
+void* GetMapKey_Custom(const WideStringView& wsKey) {
+  uint32_t dwKey = FX_HashCode_GetW(wsKey, false);
+  return (void*)(uintptr_t)((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);
+}
+
+void XFA_DefaultFreeData(void* pData) {}
+
+XFA_MAPDATABLOCKCALLBACKINFO gs_XFADefaultFreeData = {XFA_DefaultFreeData,
+                                                      nullptr};
+
+}  // namespace
+
+struct XFA_MAPDATABLOCK {
+  uint8_t* GetData() const { return (uint8_t*)this + sizeof(XFA_MAPDATABLOCK); }
+
+  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();
+}
+
+void CJX_Object::DefineMethods(const CJX_MethodSpec method_specs[]) {
+  for (size_t i = 0; method_specs[i].pMethodCall != nullptr; ++i)
+    method_specs_[method_specs[i].pName] = method_specs[i].pMethodCall;
+}
+
+CXFA_Document* CJX_Object::GetDocument() const {
+  return object_->GetDocument();
+}
+
+void CJX_Object::Script_ObjectClass_ClassName(CFXJSE_Value* pValue,
+                                              bool bSetting,
+                                              XFA_Attribute eAttribute) {
+  if (bSetting) {
+    ThrowInvalidPropertyException();
+    return;
+  }
+  pValue->SetString(
+      FX_UTF8Encode(GetXFAObject()->GetClassName()).AsStringView());
+}
+
+bool CJX_Object::HasMethod(const WideString& func) const {
+  return pdfium::ContainsKey(method_specs_, func.UTF8Encode());
+}
+
+CJS_Return CJX_Object::RunMethod(
+    const WideString& func,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  auto it = method_specs_.find(func.UTF8Encode());
+  if (it == method_specs_.end())
+    return CJS_Return(false);
+  return it->second(this, GetXFAObject()->GetDocument()->GetScriptContext(),
+                    params);
+}
+
+void CJX_Object::ThrowInvalidPropertyException() const {
+  ThrowException(L"Invalid property set operation.");
+}
+
+void CJX_Object::ThrowIndexOutOfBoundsException() const {
+  ThrowException(L"Index value is out of bounds.");
+}
+
+void CJX_Object::ThrowParamCountMismatchException(
+    const WideString& method) const {
+  ThrowException(L"Incorrect number of parameters calling method '%.16s'.",
+                 method.c_str());
+}
+
+void CJX_Object::ThrowArgumentMismatchException() const {
+  ThrowException(L"Argument mismatch in property or function argument.");
+}
+
+void CJX_Object::ThrowException(const wchar_t* str, ...) const {
+  va_list arg_ptr;
+  va_start(arg_ptr, str);
+  WideString wsMessage = WideString::FormatV(str, arg_ptr);
+  va_end(arg_ptr);
+
+  ASSERT(!wsMessage.IsEmpty());
+  FXJSE_ThrowMessage(wsMessage.UTF8Encode().AsStringView());
+}
+
+bool CJX_Object::HasAttribute(XFA_Attribute eAttr) {
+  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  return HasMapModuleKey(pKey);
+}
+
+bool CJX_Object::SetAttribute(XFA_Attribute eAttr,
+                              const WideStringView& wsValue,
+                              bool bNotify) {
+  switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) {
+    case XFA_AttributeType::Enum: {
+      pdfium::Optional<XFA_AttributeEnum> item =
+          CXFA_Node::NameToAttributeEnum(wsValue);
+      return SetEnum(
+          eAttr,
+          item ? *item : *(ToNode(GetXFAObject())->GetDefaultEnum(eAttr)),
+          bNotify);
+    }
+    case XFA_AttributeType::CData:
+      return SetCData(eAttr, WideString(wsValue), bNotify, false);
+    case XFA_AttributeType::Boolean:
+      return SetBoolean(eAttr, wsValue != L"0", bNotify);
+    case XFA_AttributeType::Integer:
+      return SetInteger(eAttr,
+                        FXSYS_round(FXSYS_wcstof(wsValue.unterminated_c_str(),
+                                                 wsValue.GetLength(), nullptr)),
+                        bNotify);
+    case XFA_AttributeType::Measure:
+      return SetMeasure(eAttr, CXFA_Measurement(wsValue), bNotify);
+    default:
+      break;
+  }
+  return false;
+}
+
+void CJX_Object::SetMapModuleString(void* pKey, const WideStringView& wsValue) {
+  SetMapModuleBuffer(pKey, (void*)wsValue.unterminated_c_str(),
+                     wsValue.GetLength() * sizeof(wchar_t), nullptr);
+}
+
+bool CJX_Object::SetAttribute(const WideStringView& wsAttr,
+                              const WideStringView& wsValue,
+                              bool bNotify) {
+  XFA_Attribute attr = CXFA_Node::NameToAttribute(wsValue);
+  if (attr != XFA_Attribute::Unknown)
+    return SetAttribute(attr, wsValue, bNotify);
+
+  void* pKey = GetMapKey_Custom(wsAttr);
+  SetMapModuleString(pKey, wsValue);
+  return true;
+}
+
+WideString CJX_Object::GetAttribute(const WideStringView& attr) {
+  return TryAttribute(attr, true).value_or(WideString());
+}
+
+WideString CJX_Object::GetAttribute(XFA_Attribute attr) {
+  return TryAttribute(attr, true).value_or(WideString());
+}
+
+pdfium::Optional<WideString> CJX_Object::TryAttribute(XFA_Attribute eAttr,
+                                                      bool bUseDefault) {
+  switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) {
+    case XFA_AttributeType::Enum: {
+      pdfium::Optional<XFA_AttributeEnum> value = TryEnum(eAttr, bUseDefault);
+      if (!value)
+        return {};
+
+      return {CXFA_Node::AttributeEnumToName(*value)};
+    }
+    case XFA_AttributeType::CData:
+      return TryCData(eAttr, bUseDefault);
+
+    case XFA_AttributeType::Boolean: {
+      pdfium::Optional<bool> value = TryBoolean(eAttr, bUseDefault);
+      if (!value)
+        return {};
+      return {*value ? L"1" : L"0"};
+    }
+    case XFA_AttributeType::Integer: {
+      pdfium::Optional<int32_t> iValue = TryInteger(eAttr, bUseDefault);
+      if (!iValue)
+        return {};
+      return {WideString::Format(L"%d", *iValue)};
+    }
+    case XFA_AttributeType::Measure: {
+      pdfium::Optional<CXFA_Measurement> value = TryMeasure(eAttr, bUseDefault);
+      if (!value)
+        return {};
+
+      return {value->ToString()};
+    }
+    default:
+      break;
+  }
+  return {};
+}
+
+pdfium::Optional<WideString> CJX_Object::TryAttribute(
+    const WideStringView& wsAttr,
+    bool bUseDefault) {
+  XFA_Attribute attr = CXFA_Node::NameToAttribute(wsAttr);
+  if (attr != XFA_Attribute::Unknown)
+    return TryAttribute(attr, bUseDefault);
+
+  void* pKey = GetMapKey_Custom(wsAttr);
+  WideStringView wsValueC;
+  if (!GetMapModuleString(pKey, wsValueC))
+    return {};
+
+  return {WideString(wsValueC)};
+}
+
+void CJX_Object::RemoveAttribute(const WideStringView& wsAttr) {
+  void* pKey = GetMapKey_Custom(wsAttr);
+  if (pKey)
+    RemoveMapModuleKey(pKey);
+}
+
+pdfium::Optional<bool> CJX_Object::TryBoolean(XFA_Attribute eAttr,
+                                              bool bUseDefault) {
+  void* pValue = nullptr;
+  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  if (GetMapModuleValue(pKey, pValue))
+    return {!!pValue};
+  if (!bUseDefault)
+    return {};
+
+  return ToNode(GetXFAObject())->GetDefaultBoolean(eAttr);
+}
+
+bool CJX_Object::SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify) {
+  CFX_XMLElement* elem = SetValue(eAttr, XFA_AttributeType::Boolean,
+                                  (void*)(uintptr_t)bValue, bNotify);
+  if (elem)
+    elem->SetString(CXFA_Node::AttributeToName(eAttr), bValue ? L"1" : L"0");
+  return true;
+}
+
+bool CJX_Object::GetBoolean(XFA_Attribute eAttr) {
+  return TryBoolean(eAttr, true).value_or(false);
+}
+
+bool CJX_Object::SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify) {
+  CFX_XMLElement* elem = SetValue(eAttr, XFA_AttributeType::Integer,
+                                  (void*)(uintptr_t)iValue, bNotify);
+  if (elem) {
+    elem->SetString(CXFA_Node::AttributeToName(eAttr),
+                    WideString::Format(L"%d", iValue));
+  }
+  return true;
+}
+
+int32_t CJX_Object::GetInteger(XFA_Attribute eAttr) {
+  return TryInteger(eAttr, true).value_or(0);
+}
+
+pdfium::Optional<int32_t> CJX_Object::TryInteger(XFA_Attribute eAttr,
+                                                 bool bUseDefault) {
+  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  void* pValue = nullptr;
+  if (GetMapModuleValue(pKey, pValue))
+    return {static_cast<int32_t>(reinterpret_cast<uintptr_t>(pValue))};
+  if (!bUseDefault)
+    return {};
+
+  return ToNode(GetXFAObject())->GetDefaultInteger(eAttr);
+}
+
+pdfium::Optional<XFA_AttributeEnum> CJX_Object::TryEnum(XFA_Attribute eAttr,
+                                                        bool bUseDefault) {
+  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  void* pValue = nullptr;
+  if (GetMapModuleValue(pKey, pValue)) {
+    return {
+        static_cast<XFA_AttributeEnum>(reinterpret_cast<uintptr_t>(pValue))};
+  }
+  if (!bUseDefault)
+    return {};
+
+  return ToNode(GetXFAObject())->GetDefaultEnum(eAttr);
+}
+
+bool CJX_Object::SetEnum(XFA_Attribute eAttr,
+                         XFA_AttributeEnum eValue,
+                         bool bNotify) {
+  CFX_XMLElement* elem = SetValue(eAttr, XFA_AttributeType::Enum,
+                                  (void*)(uintptr_t)eValue, bNotify);
+  if (elem) {
+    elem->SetString(CXFA_Node::AttributeToName(eAttr),
+                    CXFA_Node::AttributeEnumToName(eValue));
+  }
+  return true;
+}
+
+XFA_AttributeEnum CJX_Object::GetEnum(XFA_Attribute eAttr) {
+  return TryEnum(eAttr, true).value_or(XFA_AttributeEnum::Unknown);
+}
+
+bool CJX_Object::SetMeasure(XFA_Attribute eAttr,
+                            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);
+  return true;
+}
+
+pdfium::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, true) &&
+      iBytes == sizeof(CXFA_Measurement)) {
+    return {*reinterpret_cast<CXFA_Measurement*>(pValue)};
+  }
+  if (!bUseDefault)
+    return {};
+
+  return ToNode(GetXFAObject())->GetDefaultMeasurement(eAttr);
+}
+
+CXFA_Measurement CJX_Object::GetMeasure(XFA_Attribute eAttr) const {
+  return TryMeasure(eAttr, true).value_or(CXFA_Measurement());
+}
+
+WideString CJX_Object::GetCData(XFA_Attribute eAttr) {
+  return TryCData(eAttr, true).value_or(WideString());
+}
+
+bool CJX_Object::SetCData(XFA_Attribute eAttr,
+                          const WideString& wsValue,
+                          bool bNotify,
+                          bool bScriptModify) {
+  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  OnChanging(eAttr, bNotify);
+  if (eAttr == XFA_Attribute::Value) {
+    WideString* pClone = new WideString(wsValue);
+    SetUserData(pKey, pClone, &deleteWideStringCallBack);
+  } else {
+    SetMapModuleString(pKey, wsValue.AsStringView());
+    if (eAttr == XFA_Attribute::Name)
+      ToNode(GetXFAObject())->UpdateNameHash();
+  }
+  OnChanged(eAttr, bNotify, bScriptModify);
+
+  if (!ToNode(GetXFAObject())->IsNeedSavingXMLNode() ||
+      eAttr == XFA_Attribute::QualifiedName ||
+      eAttr == XFA_Attribute::BindingNode) {
+    return true;
+  }
+
+  if (eAttr == XFA_Attribute::Name &&
+      (GetXFAObject()->GetElementType() == XFA_Element::DataValue ||
+       GetXFAObject()->GetElementType() == XFA_Element::DataGroup)) {
+    return true;
+  }
+
+  auto* elem =
+      static_cast<CFX_XMLElement*>(ToNode(GetXFAObject())->GetXMLMappingNode());
+  if (eAttr == XFA_Attribute::Value) {
+    FX_XMLNODETYPE eXMLType = elem->GetType();
+    switch (eXMLType) {
+      case FX_XMLNODE_Element:
+        if (ToNode(GetXFAObject())->IsAttributeInXML()) {
+          elem->SetString(WideString(GetCData(XFA_Attribute::QualifiedName)),
+                          wsValue);
+        } else {
+          bool bDeleteChildren = true;
+          if (ToNode(GetXFAObject())->GetPacketType() ==
+              XFA_PacketType::Datasets) {
+            for (CXFA_Node* pChildDataNode =
+                     ToNode(GetXFAObject())
+                         ->GetNodeItem(XFA_NODEITEM_FirstChild);
+                 pChildDataNode; pChildDataNode = pChildDataNode->GetNodeItem(
+                                     XFA_NODEITEM_NextSibling)) {
+              if (!pChildDataNode->GetBindItems()->empty()) {
+                bDeleteChildren = false;
+                break;
+              }
+            }
+          }
+          if (bDeleteChildren)
+            elem->DeleteChildren();
+
+          elem->SetTextData(wsValue);
+        }
+        break;
+      case FX_XMLNODE_Text:
+        static_cast<CFX_XMLText*>(ToNode(GetXFAObject())->GetXMLMappingNode())
+            ->SetText(wsValue);
+        break;
+      default:
+        NOTREACHED();
+    }
+    return true;
+  }
+  ASSERT(elem->GetType() == FX_XMLNODE_Element);
+
+  WideString wsAttrName = CXFA_Node::AttributeToName(eAttr);
+  if (eAttr == XFA_Attribute::ContentType)
+    wsAttrName = L"xfa:" + wsAttrName;
+
+  elem->SetString(wsAttrName, wsValue);
+  return true;
+}
+
+void CJX_Object::SetAttributeValue(const WideString& wsValue,
+                                   const WideString& wsXMLValue,
+                                   bool bNotify,
+                                   bool bScriptModify) {
+  void* pKey =
+      GetMapKey_Element(GetXFAObject()->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 (!ToNode(GetXFAObject())->IsNeedSavingXMLNode())
+    return;
+
+  auto* elem =
+      static_cast<CFX_XMLElement*>(ToNode(GetXFAObject())->GetXMLMappingNode());
+  FX_XMLNODETYPE eXMLType = elem->GetType();
+  switch (eXMLType) {
+    case FX_XMLNODE_Element:
+      if (ToNode(GetXFAObject())->IsAttributeInXML()) {
+        elem->SetString(WideString(GetCData(XFA_Attribute::QualifiedName)),
+                        wsXMLValue);
+      } else {
+        bool bDeleteChildren = true;
+        if (ToNode(GetXFAObject())->GetPacketType() ==
+            XFA_PacketType::Datasets) {
+          for (CXFA_Node* pChildDataNode =
+                   ToNode(GetXFAObject())->GetNodeItem(XFA_NODEITEM_FirstChild);
+               pChildDataNode; pChildDataNode = pChildDataNode->GetNodeItem(
+                                   XFA_NODEITEM_NextSibling)) {
+            if (!pChildDataNode->GetBindItems()->empty()) {
+              bDeleteChildren = false;
+              break;
+            }
+          }
+        }
+        if (bDeleteChildren)
+          elem->DeleteChildren();
+
+        elem->SetTextData(wsXMLValue);
+      }
+      break;
+    case FX_XMLNODE_Text:
+      static_cast<CFX_XMLText*>(ToNode(GetXFAObject())->GetXMLMappingNode())
+          ->SetText(wsXMLValue);
+      break;
+    default:
+      ASSERT(0);
+  }
+}
+
+pdfium::Optional<WideString> CJX_Object::TryCData(XFA_Attribute eAttr,
+                                                  bool bUseDefault) {
+  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, true) &&
+        iBytes == sizeof(void*)) {
+      memcpy(&pData, pData, iBytes);
+      pStr = reinterpret_cast<WideString*>(pData);
+    }
+    if (pStr)
+      return {*pStr};
+  } else {
+    WideStringView wsValueC;
+    if (GetMapModuleString(pKey, wsValueC))
+      return {WideString(wsValueC)};
+  }
+  if (!bUseDefault)
+    return {};
+
+  return ToNode(GetXFAObject())->GetDefaultCData(eAttr);
+}
+
+CFX_XMLElement* CJX_Object::SetValue(XFA_Attribute eAttr,
+                                     XFA_AttributeType eType,
+                                     void* pValue,
+                                     bool bNotify) {
+  void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr);
+  OnChanging(eAttr, bNotify);
+  SetMapModuleValue(pKey, pValue);
+  OnChanged(eAttr, bNotify, false);
+  if (!ToNode(GetXFAObject())->IsNeedSavingXMLNode())
+    return nullptr;
+
+  auto* elem =
+      static_cast<CFX_XMLElement*>(ToNode(GetXFAObject())->GetXMLMappingNode());
+  ASSERT(elem->GetType() == FX_XMLNODE_Element);
+
+  return elem;
+}
+
+bool CJX_Object::SetUserData(void* pKey,
+                             void* pData,
+                             XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) {
+  SetMapModuleBuffer(pKey, &pData, sizeof(void*),
+                     pCallbackInfo ? pCallbackInfo : &gs_XFADefaultFreeData);
+  return true;
+}
+
+XFA_MAPMODULEDATA* CJX_Object::CreateMapModuleData() {
+  if (!map_module_data_)
+    map_module_data_ = pdfium::MakeUnique<XFA_MAPMODULEDATA>();
+  return map_module_data_.get();
+}
+
+XFA_MAPMODULEDATA* CJX_Object::GetMapModuleData() const {
+  return map_module_data_.get();
+}
+
+void CJX_Object::SetMapModuleValue(void* pKey, void* pValue) {
+  CreateMapModuleData()->m_ValueMap[pKey] = pValue;
+}
+
+bool CJX_Object::GetMapModuleValue(void* pKey, void*& pValue) {
+  for (CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
+       pNode = pNode->GetTemplateNode()) {
+    XFA_MAPMODULEDATA* pModule = pNode->JSNode()->GetMapModuleData();
+    if (pModule) {
+      auto it = pModule->m_ValueMap.find(pKey);
+      if (it != pModule->m_ValueMap.end()) {
+        pValue = it->second;
+        return true;
+      }
+    }
+    if (pNode->GetPacketType() == XFA_PacketType::Datasets)
+      break;
+  }
+  return false;
+}
+
+bool CJX_Object::GetMapModuleString(void* pKey, WideStringView& wsValue) {
+  void* pValue;
+  int32_t iBytes;
+  if (!GetMapModuleBuffer(pKey, pValue, iBytes, true))
+    return false;
+
+  // Defensive measure: no out-of-bounds pointers even if zero length.
+  int32_t iChars = iBytes / sizeof(wchar_t);
+  wsValue = WideStringView(iChars ? (const wchar_t*)pValue : nullptr, iChars);
+  return true;
+}
+
+void CJX_Object::SetMapModuleBuffer(
+    void* pKey,
+    void* pValue,
+    int32_t iBytes,
+    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& iBytes,
+                                    bool bProtoAlso) const {
+  XFA_MAPDATABLOCK* pBuffer = nullptr;
+  for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode;
+       pNode = pNode->GetTemplateNode()) {
+    XFA_MAPMODULEDATA* pModule = pNode->JSNode()->GetMapModuleData();
+    if (pModule) {
+      auto it = pModule->m_BufferMap.find(pKey);
+      if (it != pModule->m_BufferMap.end()) {
+        pBuffer = it->second;
+        break;
+      }
+    }
+    if (!bProtoAlso || pNode->GetPacketType() == XFA_PacketType::Datasets)
+      break;
+  }
+  if (!pBuffer)
+    return false;
+
+  pValue = pBuffer->GetData();
+  iBytes = pBuffer->iBytes;
+  return true;
+}
+
+bool CJX_Object::HasMapModuleKey(void* pKey) {
+  XFA_MAPMODULEDATA* pModule = GetMapModuleData();
+  return pModule && (pdfium::ContainsKey(pModule->m_ValueMap, pKey) ||
+                     pdfium::ContainsKey(pModule->m_BufferMap, pKey));
+}
+
+void CJX_Object::ClearMapModuleBuffer() {
+  XFA_MAPMODULEDATA* pModule = GetMapModuleData();
+  if (!pModule)
+    return;
+
+  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);
+    }
+  }
+  pModule->m_BufferMap.clear();
+  pModule->m_ValueMap.clear();
+}
+
+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;
+}
+
+void CJX_Object::MergeAllData(CXFA_Object* pDstModule) {
+  XFA_MAPMODULEDATA* pDstModuleData =
+      ToNode(pDstModule)->JSNode()->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::MoveBufferMapData(CXFA_Object* pDstModule) {
+  if (!pDstModule)
+    return;
+
+  bool bNeedMove = true;
+  if (pDstModule->GetElementType() != GetXFAObject()->GetElementType())
+    bNeedMove = false;
+
+  if (bNeedMove)
+    ToNode(pDstModule)->JSNode()->SetCalcData(ReleaseCalcData());
+  if (!pDstModule->IsNodeV())
+    return;
+
+  WideString wsValue = ToNode(pDstModule)->JSNode()->GetContent(false);
+  WideString wsFormatValue(wsValue);
+  CXFA_WidgetData* pWidgetData = ToNode(pDstModule)->GetContainerWidgetData();
+  if (pWidgetData)
+    wsFormatValue = pWidgetData->GetFormatDataValue(wsValue);
+
+  ToNode(pDstModule)
+      ->JSNode()
+      ->SetContent(wsValue, wsFormatValue, true, true, true);
+}
+
+void CJX_Object::MoveBufferMapData(CXFA_Object* pSrcModule,
+                                   CXFA_Object* pDstModule) {
+  if (!pSrcModule || !pDstModule)
+    return;
+
+  CXFA_Node* pSrcChild =
+      ToNode(pSrcModule)->GetNodeItem(XFA_NODEITEM_FirstChild);
+  CXFA_Node* pDstChild =
+      ToNode(pDstModule)->GetNodeItem(XFA_NODEITEM_FirstChild);
+  while (pSrcChild && pDstChild) {
+    MoveBufferMapData(pSrcChild, pDstChild);
+
+    pSrcChild = pSrcChild->GetNodeItem(XFA_NODEITEM_NextSibling);
+    pDstChild = pDstChild->GetNodeItem(XFA_NODEITEM_NextSibling);
+  }
+  ToNode(pSrcModule)->JSNode()->MoveBufferMapData(pDstModule);
+}
+
+void CJX_Object::OnChanging(XFA_Attribute eAttr, bool bNotify) {
+  if (!bNotify || !ToNode(GetXFAObject())->IsInitialized())
+    return;
+
+  CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
+  if (pNotify)
+    pNotify->OnValueChanging(ToNode(GetXFAObject()), 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::SetCalcData(std::unique_ptr<CXFA_CalcData> data) {
+  calc_data_ = std::move(data);
+}
+
+std::unique_ptr<CXFA_CalcData> CJX_Object::ReleaseCalcData() {
+  return std::move(calc_data_);
+}
diff --git a/fxjs/xfa/cjx_object.h b/fxjs/xfa/cjx_object.h
new file mode 100644
index 0000000..cb8350d
--- /dev/null
+++ b/fxjs/xfa/cjx_object.h
@@ -0,0 +1,163 @@
+// Copyright 2017 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_CJX_OBJECT_H_
+#define FXJS_XFA_CJX_OBJECT_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "core/fxcrt/xml/cfx_xmlelement.h"
+#include "third_party/base/optional.h"
+#include "xfa/fxfa/fxfa_basic.h"
+
+class CFXJSE_Value;
+class CJS_V8;
+class CXFA_CalcData;
+class CXFA_Document;
+class CXFA_Object;
+struct XFA_MAPMODULEDATA;
+
+typedef CJS_Return (*CJX_MethodCall)(
+    CJX_Object* obj,
+    CJS_V8* 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;
+};
+
+class CJX_Object {
+ public:
+  explicit CJX_Object(CXFA_Object* obj);
+  virtual ~CJX_Object();
+
+  CXFA_Object* GetXFAObject() { return object_.Get(); }
+  const CXFA_Object* GetXFAObject() const { return object_.Get(); }
+
+  CXFA_Document* GetDocument() const;
+
+  bool HasMethod(const WideString& func) const;
+  CJS_Return RunMethod(const WideString& func,
+                       const std::vector<v8::Local<v8::Value>>& params);
+
+  bool HasAttribute(XFA_Attribute eAttr);
+  bool SetAttribute(XFA_Attribute eAttr,
+                    const WideStringView& wsValue,
+                    bool bNotify);
+  bool SetAttribute(const WideStringView& wsAttr,
+                    const WideStringView& wsValue,
+                    bool bNotify);
+  void RemoveAttribute(const WideStringView& wsAttr);
+  WideString GetAttribute(const WideStringView& attr);
+  WideString GetAttribute(XFA_Attribute attr);
+  pdfium::Optional<WideString> TryAttribute(const WideStringView& wsAttr,
+                                            bool bUseDefault);
+  pdfium::Optional<WideString> TryAttribute(XFA_Attribute eAttr,
+                                            bool bUseDefault);
+
+  void SetAttributeValue(const WideString& wsValue,
+                         const WideString& wsXMLValue,
+                         bool bNotify,
+                         bool bScriptModify);
+
+  pdfium::Optional<int32_t> TryInteger(XFA_Attribute eAttr, bool bUseDefault);
+  bool SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify);
+  int32_t GetInteger(XFA_Attribute eAttr);
+
+  pdfium::Optional<WideString> TryCData(XFA_Attribute eAttr, bool bUseDefault);
+  bool SetCData(XFA_Attribute eAttr,
+                const WideString& wsValue,
+                bool bNotify,
+                bool bScriptModify);
+  WideString GetCData(XFA_Attribute eAttr);
+
+  pdfium::Optional<XFA_AttributeEnum> TryEnum(XFA_Attribute eAttr,
+                                              bool bUseDefault);
+  bool SetEnum(XFA_Attribute eAttr, XFA_AttributeEnum eValue, bool bNotify);
+  XFA_AttributeEnum GetEnum(XFA_Attribute eAttr);
+
+  pdfium::Optional<bool> TryBoolean(XFA_Attribute eAttr, bool bUseDefault);
+  bool SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify);
+  bool GetBoolean(XFA_Attribute eAttr);
+
+  pdfium::Optional<CXFA_Measurement> TryMeasure(XFA_Attribute eAttr,
+                                                bool bUseDefault) const;
+  bool SetMeasure(XFA_Attribute eAttr, CXFA_Measurement mValue, bool bNotify);
+  CXFA_Measurement GetMeasure(XFA_Attribute eAttr) const;
+
+  void Script_ObjectClass_ClassName(CFXJSE_Value* pValue,
+                                    bool bSetting,
+                                    XFA_Attribute eAttribute);
+
+  void MergeAllData(CXFA_Object* pDstModule);
+
+  void SetCalcData(std::unique_ptr<CXFA_CalcData> data);
+  CXFA_CalcData* GetCalcData() const { return calc_data_.get(); }
+  std::unique_ptr<CXFA_CalcData> ReleaseCalcData();
+
+  void ThrowInvalidPropertyException() const;
+  void ThrowArgumentMismatchException() const;
+  void ThrowIndexOutOfBoundsException() const;
+  void ThrowParamCountMismatchException(const WideString& method) const;
+
+ protected:
+  void DefineMethods(const CJX_MethodSpec method_specs[]);
+
+  void MoveBufferMapData(CXFA_Object* pSrcModule, CXFA_Object* pDstModule);
+  void SetMapModuleString(void* pKey, const WideStringView& wsValue);
+  void ThrowException(const wchar_t* str, ...) const;
+
+ private:
+  void OnChanged(XFA_Attribute eAttr, bool bNotify, bool bScriptModify);
+  void OnChanging(XFA_Attribute eAttr, bool bNotify);
+  bool SetUserData(void* pKey,
+                   void* pData,
+                   XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
+
+  // 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,
+                           XFA_AttributeType eType,
+                           void* pValue,
+                           bool bNotify);
+
+  XFA_MAPMODULEDATA* CreateMapModuleData();
+  XFA_MAPMODULEDATA* GetMapModuleData() const;
+  void SetMapModuleValue(void* pKey, void* pValue);
+  bool GetMapModuleValue(void* pKey, void*& pValue);
+  bool GetMapModuleString(void* pKey, WideStringView& wsValue);
+  void SetMapModuleBuffer(void* pKey,
+                          void* pValue,
+                          int32_t iBytes,
+                          XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo);
+  bool GetMapModuleBuffer(void* pKey,
+                          void*& pValue,
+                          int32_t& iBytes,
+                          bool bProtoAlso) const;
+  bool HasMapModuleKey(void* pKey);
+  void ClearMapModuleBuffer();
+  void RemoveMapModuleKey(void* pKey);
+  void MoveBufferMapData(CXFA_Object* pDstModule);
+
+  UnownedPtr<CXFA_Object> object_;
+  std::unique_ptr<XFA_MAPMODULEDATA> map_module_data_;
+  std::unique_ptr<CXFA_CalcData> calc_data_;
+  std::map<ByteString, CJX_MethodCall> method_specs_;
+};
+
+#endif  // FXJS_XFA_CJX_OBJECT_H_
diff --git a/fxjs/xfa/cjx_packet.h b/fxjs/xfa/cjx_packet.h
index 80520e2..b014a9d 100644
--- a/fxjs/xfa/cjx_packet.h
+++ b/fxjs/xfa/cjx_packet.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_PACKET_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_Packet;
 
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.cpp b/fxjs/xfa/cjx_signaturepseudomodel.cpp
new file mode 100644
index 0000000..6b39ac2
--- /dev/null
+++ b/fxjs/xfa/cjx_signaturepseudomodel.cpp
@@ -0,0 +1,60 @@
+// Copyright 2017 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/cjx_signaturepseudomodel.h"
+
+#include <vector>
+
+#include "fxjs/cfxjse_value.h"
+#include "fxjs/js_resources.h"
+#include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
+
+const CJX_MethodSpec CJX_SignaturePseudoModel::MethodSpecs[] = {
+    {"verify", verifySignature_static},
+    {"sign", sign_static},
+    {"enumerate", enumerate_static},
+    {"clear", clear_static},
+    {"", nullptr}};
+
+CJX_SignaturePseudoModel::CJX_SignaturePseudoModel(
+    CScript_SignaturePseudoModel* model)
+    : CJX_Object(model) {
+  DefineMethods(MethodSpecs);
+}
+
+CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() {}
+
+CJS_Return CJX_SignaturePseudoModel::verifySignature(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 4)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  return CJS_Return(runtime->NewNumber(0));
+}
+
+CJS_Return CJX_SignaturePseudoModel::sign(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.size() < 3 || params.size() > 7)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  return CJS_Return(runtime->NewBoolean(false));
+}
+
+CJS_Return CJX_SignaturePseudoModel::enumerate(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (!params.empty())
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  return CJS_Return(true);
+}
+
+CJS_Return CJX_SignaturePseudoModel::clear(
+    CJS_V8* runtime,
+    const std::vector<v8::Local<v8::Value>>& params) {
+  if (params.empty() || params.size() > 2)
+    return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
+  return CJS_Return(runtime->NewBoolean(false));
+}
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.h b/fxjs/xfa/cjx_signaturepseudomodel.h
new file mode 100644
index 0000000..88f90fb
--- /dev/null
+++ b/fxjs/xfa/cjx_signaturepseudomodel.h
@@ -0,0 +1,29 @@
+// Copyright 2017 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_CJX_SIGNATUREPSEUDOMODEL_H_
+#define FXJS_XFA_CJX_SIGNATUREPSEUDOMODEL_H_
+
+#include "fxjs/CJX_Define.h"
+#include "fxjs/xfa/cjx_object.h"
+
+class CScript_SignaturePseudoModel;
+
+class CJX_SignaturePseudoModel : public CJX_Object {
+ public:
+  explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model);
+  ~CJX_SignaturePseudoModel() override;
+
+  JS_METHOD(verifySignature /*verify*/, CJX_SignaturePseudoModel);
+  JS_METHOD(sign, CJX_SignaturePseudoModel);
+  JS_METHOD(enumerate, CJX_SignaturePseudoModel);
+  JS_METHOD(clear, CJX_SignaturePseudoModel);
+
+ private:
+  static const CJX_MethodSpec MethodSpecs[];
+};
+
+#endif  // FXJS_XFA_CJX_SIGNATUREPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_source.h b/fxjs/xfa/cjx_source.h
index 1502e0e..8b35fac 100644
--- a/fxjs/xfa/cjx_source.h
+++ b/fxjs/xfa/cjx_source.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_SOURCE_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_Source;
 
diff --git a/fxjs/xfa/cjx_textnode.h b/fxjs/xfa/cjx_textnode.h
index 1fa84d8..c0a01d6 100644
--- a/fxjs/xfa/cjx_textnode.h
+++ b/fxjs/xfa/cjx_textnode.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_TEXTNODE_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_Node;
 
diff --git a/fxjs/xfa/cjx_tree.h b/fxjs/xfa/cjx_tree.h
index 3008112..72468d6 100644
--- a/fxjs/xfa/cjx_tree.h
+++ b/fxjs/xfa/cjx_tree.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_TREE_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_object.h"
+#include "fxjs/xfa/cjx_object.h"
 
 class CXFA_Object;
 class CXFA_Node;
diff --git a/fxjs/xfa/cjx_wsdlconnection.h b/fxjs/xfa/cjx_wsdlconnection.h
index 76533ce..8276017 100644
--- a/fxjs/xfa/cjx_wsdlconnection.h
+++ b/fxjs/xfa/cjx_wsdlconnection.h
@@ -8,7 +8,7 @@
 #define FXJS_XFA_CJX_WSDLCONNECTION_H_
 
 #include "fxjs/CJX_Define.h"
-#include "fxjs/cjx_node.h"
+#include "fxjs/xfa/cjx_node.h"
 
 class CXFA_WsdlConnection;