Clear v8 object binding in CXFA_Object destructor.

Noticed this possibility while looking at another issue,
this is a best practice to ensure validity.

- Rename some functions for clarity.
- Insist on a non-null return for GetOrCreate...()
- Handle dependence in CFXA_Object destruction on the object
  that owns it (and may be cleaning it up in its own destructor).

Change-Id: I129817f636455f630d9d911f6fff5022faa72e7b
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/52850
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/fxjs/xfa/cfxjse_context.cpp b/fxjs/xfa/cfxjse_context.cpp
index 5dec89c..7a3b593 100644
--- a/fxjs/xfa/cfxjse_context.cpp
+++ b/fxjs/xfa/cfxjse_context.cpp
@@ -144,6 +144,13 @@
   hObject->SetAlignedPointerInInternalField(1, lpNewBinding);
 }
 
+void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hObject) {
+  ASSERT(!hObject.IsEmpty());
+  ASSERT(hObject->InternalFieldCount() == 2);
+  hObject->SetAlignedPointerInInternalField(0, nullptr);
+  hObject->SetAlignedPointerInInternalField(1, nullptr);
+}
+
 CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(
     v8::Local<v8::Object> hJSObject) {
   ASSERT(!hJSObject.IsEmpty());
diff --git a/fxjs/xfa/cfxjse_context.h b/fxjs/xfa/cfxjse_context.h
index 347a69d..b519e77 100644
--- a/fxjs/xfa/cfxjse_context.h
+++ b/fxjs/xfa/cfxjse_context.h
@@ -52,6 +52,7 @@
 void FXJSE_UpdateObjectBinding(v8::Local<v8::Object> hObject,
                                CFXJSE_HostObject* lpNewBinding);
 
+void FXJSE_ClearObjectBinding(v8::Local<v8::Object> hJSObject);
 CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local<v8::Object> hJSObject);
 
 #endif  // FXJS_XFA_CFXJSE_CONTEXT_H_
diff --git a/fxjs/xfa/cfxjse_engine.cpp b/fxjs/xfa/cfxjse_engine.cpp
index 8c20211..e4675fd 100644
--- a/fxjs/xfa/cfxjse_engine.cpp
+++ b/fxjs/xfa/cfxjse_engine.cpp
@@ -118,6 +118,9 @@
 CFXJSE_Engine::~CFXJSE_Engine() {
   for (const auto& pair : m_mapVariableToContext)
     delete ToThisProxy(pair.second->GetGlobalObject().get());
+
+  for (const auto& pair : m_mapObjectToValue)
+    pair.second->ClearHostObject();
 }
 
 bool CFXJSE_Engine::RunScript(CXFA_Script::Type eScriptType,
@@ -144,7 +147,8 @@
   AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
   m_pThisObject = pThisObject;
 
-  CFXJSE_Value* pValue = pThisObject ? GetJSValueFromMap(pThisObject) : nullptr;
+  CFXJSE_Value* pValue =
+      pThisObject ? GetOrCreateJSBindingFromMap(pThisObject) : nullptr;
   IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime.Get());
   return m_JsContext->ExecuteScript(btScript.c_str(), hRetValue, pValue);
 }
@@ -161,7 +165,8 @@
   if (!ResolveObjects(refNode, propname, &resolveRs, dwFlag, nullptr))
     return false;
   if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    pValue->Assign(GetJSValueFromMap(resolveRs.objects.front().Get()));
+    pValue->Assign(
+        GetOrCreateJSBindingFromMap(resolveRs.objects.front().Get()));
     return true;
   }
   if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Attribute &&
@@ -228,7 +233,7 @@
       CXFA_Object* pObj =
           lpScriptContext->GetDocument()->GetXFAObject(uHashCode);
       if (pObj) {
-        pValue->Assign(lpScriptContext->GetJSValueFromMap(pObj));
+        pValue->Assign(lpScriptContext->GetOrCreateJSBindingFromMap(pObj));
         return;
       }
     }
@@ -302,7 +307,7 @@
   CXFA_Object* pObject =
       lpScriptContext->GetVariablesThis(pOriginalObject, false);
   if (wsPropName.EqualsASCII("xfa")) {
-    CFXJSE_Value* pValue = lpScriptContext->GetJSValueFromMap(
+    CFXJSE_Value* pValue = lpScriptContext->GetOrCreateJSBindingFromMap(
         lpScriptContext->GetDocument()->GetRoot());
     pReturnValue->Assign(pValue);
     return;
@@ -739,9 +744,7 @@
   m_CacheList.push_back(std::move(pList));
 }
 
-CFXJSE_Value* CFXJSE_Engine::GetJSValueFromMap(CXFA_Object* pObject) {
-  if (!pObject)
-    return nullptr;
+CFXJSE_Value* CFXJSE_Engine::GetOrCreateJSBindingFromMap(CXFA_Object* pObject) {
   if (pObject->IsNode())
     RunVariablesScript(pObject->AsNode());
 
@@ -750,13 +753,22 @@
     return iter->second.get();
 
   auto jsValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
-  jsValue->SetObject(pObject, m_pJsClass.Get());
+  jsValue->SetHostObject(pObject, m_pJsClass.Get());
 
   CFXJSE_Value* pValue = jsValue.get();
   m_mapObjectToValue.insert(std::make_pair(pObject, std::move(jsValue)));
   return pValue;
 }
 
+void CFXJSE_Engine::RemoveJSBindingFromMap(CXFA_Object* pObject) {
+  auto iter = m_mapObjectToValue.find(pObject);
+  if (iter == m_mapObjectToValue.end())
+    return;
+
+  iter->second->ClearHostObject();
+  m_mapObjectToValue.erase(iter);
+}
+
 void CFXJSE_Engine::SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray) {
   m_pScriptNodeArray = pArray;
 }
diff --git a/fxjs/xfa/cfxjse_engine.h b/fxjs/xfa/cfxjse_engine.h
index d96eaee..addf0cc 100644
--- a/fxjs/xfa/cfxjse_engine.h
+++ b/fxjs/xfa/cfxjse_engine.h
@@ -80,7 +80,10 @@
                       XFA_RESOLVENODE_RS* resolveNodeRS,
                       uint32_t dwStyles,
                       CXFA_Node* bindNode);
-  CFXJSE_Value* GetJSValueFromMap(CXFA_Object* pObject);
+
+  CFXJSE_Value* GetOrCreateJSBindingFromMap(CXFA_Object* pObject);
+  void RemoveJSBindingFromMap(CXFA_Object* pObject);
+
   void AddToCacheList(std::unique_ptr<CXFA_List> pList);
   CXFA_Object* GetThisObject() const { return m_pThisObject.Get(); }
 
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp
index 47b52a9..6782fb6 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -5438,8 +5438,8 @@
       WideString::FromUTF8(bsAccessorName).AsStringView(), &resolveNodeRS,
       dwFlags, nullptr);
   if (bRet && resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
-    accessorValue->Assign(
-        pScriptContext->GetJSValueFromMap(resolveNodeRS.objects.front().Get()));
+    accessorValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(
+        resolveNodeRS.objects.front().Get()));
     return true;
   }
   return false;
@@ -5517,7 +5517,7 @@
     for (auto& pObject : resolveNodeRS.objects) {
       resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
       resultValues->back()->Assign(
-          pScriptContext->GetJSValueFromMap(pObject.Get()));
+          pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get()));
     }
     return;
   }
@@ -5713,7 +5713,7 @@
     : m_pIsolate(pScriptIsolate),
       m_pValue(pdfium::MakeUnique<CFXJSE_Value>(pScriptIsolate)),
       m_pDocument(pDoc) {
-  m_pValue->SetObject(
+  m_pValue->SetHostObject(
       this,
       CFXJSE_Class::Create(pScriptContext, &kFormCalcFM2JSDescriptor, false));
 }
diff --git a/fxjs/xfa/cfxjse_value.cpp b/fxjs/xfa/cfxjse_value.cpp
index 194e77d..eff4477 100644
--- a/fxjs/xfa/cfxjse_value.cpp
+++ b/fxjs/xfa/cfxjse_value.cpp
@@ -82,8 +82,8 @@
   return FXJSE_RetrieveObjectBinding(pValue.As<v8::Object>());
 }
 
-void CFXJSE_Value::SetObject(CFXJSE_HostObject* lpObject,
-                             CFXJSE_Class* pClass) {
+void CFXJSE_Value::SetHostObject(CFXJSE_HostObject* lpObject,
+                                 CFXJSE_Class* pClass) {
   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
   v8::Local<v8::FunctionTemplate> hClass =
       v8::Local<v8::FunctionTemplate>::New(GetIsolate(), pClass->m_hTemplate);
@@ -95,6 +95,13 @@
   m_hValue.Reset(GetIsolate(), hObject);
 }
 
+void CFXJSE_Value::ClearHostObject() {
+  CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
+  FXJSE_ClearObjectBinding(m_hValue.Get(GetIsolate()).As<v8::Object>());
+  v8::Local<v8::Value> hValue = v8::Null(GetIsolate());
+  m_hValue.Reset(GetIsolate(), hValue);
+}
+
 void CFXJSE_Value::SetArray(
     const std::vector<std::unique_ptr<CFXJSE_Value>>& values) {
   CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate());
diff --git a/fxjs/xfa/cfxjse_value.h b/fxjs/xfa/cfxjse_value.h
index 5a81a80..aea43fd 100644
--- a/fxjs/xfa/cfxjse_value.h
+++ b/fxjs/xfa/cfxjse_value.h
@@ -52,7 +52,9 @@
   void SetString(ByteStringView szString);
   void SetFloat(float fFloat);
 
-  void SetObject(CFXJSE_HostObject* lpObject, CFXJSE_Class* pClass);
+  void SetHostObject(CFXJSE_HostObject* lpObject, CFXJSE_Class* pClass);
+  void ClearHostObject();
+
   void SetArray(const std::vector<std::unique_ptr<CFXJSE_Value>>& values);
   void SetDate(double dDouble);
 
diff --git a/fxjs/xfa/cjx_exclgroup.cpp b/fxjs/xfa/cjx_exclgroup.cpp
index f24b7e6..ae20485 100644
--- a/fxjs/xfa/cjx_exclgroup.cpp
+++ b/fxjs/xfa/cjx_exclgroup.cpp
@@ -107,9 +107,8 @@
     return CJS_Result::Success(runtime->NewNull());
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pReturnNode);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pReturnNode);
 
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
diff --git a/fxjs/xfa/cjx_form.cpp b/fxjs/xfa/cjx_form.cpp
index 41f7e45..32c884b 100644
--- a/fxjs/xfa/cjx_form.cpp
+++ b/fxjs/xfa/cjx_form.cpp
@@ -48,9 +48,9 @@
 
   CXFA_ArrayNodeList* pFormNodes = new CXFA_ArrayNodeList(GetDocument());
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pFormNodes);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pFormNodes);
+
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
 }
diff --git a/fxjs/xfa/cjx_hostpseudomodel.cpp b/fxjs/xfa/cjx_hostpseudomodel.cpp
index 678b052..c5d36b3 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.cpp
+++ b/fxjs/xfa/cjx_hostpseudomodel.cpp
@@ -474,9 +474,7 @@
     return CJS_Result::Success();
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
 
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
diff --git a/fxjs/xfa/cjx_instancemanager.cpp b/fxjs/xfa/cjx_instancemanager.cpp
index 6d6de91..f3881e4 100644
--- a/fxjs/xfa/cjx_instancemanager.cpp
+++ b/fxjs/xfa/cjx_instancemanager.cpp
@@ -250,9 +250,8 @@
   }
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNewInstance);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pNewInstance);
 
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
@@ -296,9 +295,8 @@
   }
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNewInstance);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pNewInstance);
 
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
diff --git a/fxjs/xfa/cjx_model.cpp b/fxjs/xfa/cjx_model.cpp
index 66ae1ea..4fa0a2e 100644
--- a/fxjs/xfa/cjx_model.cpp
+++ b/fxjs/xfa/cjx_model.cpp
@@ -67,9 +67,7 @@
   }
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNewNode);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNewNode);
 
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp
index 59c364a..bf10c0f 100644
--- a/fxjs/xfa/cjx_node.cpp
+++ b/fxjs/xfa/cjx_node.cpp
@@ -183,9 +183,8 @@
 
   CXFA_Node* pCloneNode = GetXFANode()->Clone(runtime->ToBoolean(params[0]));
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pCloneNode);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+          pCloneNode);
 
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
@@ -219,9 +218,7 @@
     return CJS_Result::Success(runtime->NewNull());
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
 
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
@@ -470,7 +467,7 @@
     ThrowInvalidPropertyException();
     return;
   }
-  pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(
+  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
       GetXFANode()->GetModelNode()));
 }
 
@@ -509,8 +506,9 @@
   std::vector<CXFA_Node*> properties = GetXFANode()->GetNodeList(
       XFA_NODEFILTER_OneOfProperty, XFA_Element::Unknown);
   if (!properties.empty()) {
-    pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(
-        properties.front()));
+    pValue->Assign(
+        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+            properties.front()));
   }
 }
 
diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
index 7a5e082..63eb003 100644
--- a/fxjs/xfa/cjx_object.cpp
+++ b/fxjs/xfa/cjx_object.cpp
@@ -1507,8 +1507,8 @@
     return;
   }
 
-  pValue->Assign(
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pDataNode));
+  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+      pDataNode));
 }
 
 void CJX_Object::ScriptSomMandatory(CFXJSE_Value* pValue,
diff --git a/fxjs/xfa/cjx_subform.cpp b/fxjs/xfa/cjx_subform.cpp
index 17739a1..18e1508 100644
--- a/fxjs/xfa/cjx_subform.cpp
+++ b/fxjs/xfa/cjx_subform.cpp
@@ -125,6 +125,6 @@
     return;
   }
 
-  pValue->Assign(
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pInstanceMgr));
+  pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
+      pInstanceMgr));
 }
diff --git a/fxjs/xfa/cjx_tree.cpp b/fxjs/xfa/cjx_tree.cpp
index c036ab9..cc13b7a 100644
--- a/fxjs/xfa/cjx_tree.cpp
+++ b/fxjs/xfa/cjx_tree.cpp
@@ -58,9 +58,7 @@
   if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
     CXFA_Object* pObject = resolveNodeRS.objects.front().Get();
     CFXJSE_Value* value =
-        GetDocument()->GetScriptContext()->GetJSValueFromMap(pObject);
-    if (!value)
-      return CJS_Result::Success(runtime->NewNull());
+        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pObject);
 
     return CJS_Result::Success(
         value->DirectGetValue().Get(runtime->GetIsolate()));
@@ -139,7 +137,7 @@
   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
   CXFA_AttachNodeList* pNodeList =
       new CXFA_AttachNodeList(GetDocument(), ToNode(GetXFAObject()));
-  pValue->SetObject(pNodeList, pScriptContext->GetJseNormalClass());
+  pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass());
 }
 
 void CJX_Tree::parent(CFXJSE_Value* pValue,
@@ -156,7 +154,8 @@
     return;
   }
 
-  pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap(pParent));
+  pValue->Assign(
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pParent));
 }
 
 void CJX_Tree::index(CFXJSE_Value* pValue,
@@ -230,5 +229,5 @@
       }
     }
   }
-  pValue->SetObject(pNodeList, pScriptContext->GetJseNormalClass());
+  pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass());
 }
diff --git a/fxjs/xfa/cjx_treelist.cpp b/fxjs/xfa/cjx_treelist.cpp
index 04a27e7..51bbb0a 100644
--- a/fxjs/xfa/cjx_treelist.cpp
+++ b/fxjs/xfa/cjx_treelist.cpp
@@ -44,9 +44,7 @@
     return CJS_Result::Success();
 
   CFXJSE_Value* value =
-      GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode);
-  if (!value)
-    return CJS_Result::Success(runtime->NewNull());
+      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
 
   return CJS_Result::Success(
       value->DirectGetValue().Get(runtime->GetIsolate()));
diff --git a/fxjs/xfa/cjx_xfa.cpp b/fxjs/xfa/cjx_xfa.cpp
index 2d943bd..08c6a80 100644
--- a/fxjs/xfa/cjx_xfa.cpp
+++ b/fxjs/xfa/cjx_xfa.cpp
@@ -31,5 +31,5 @@
     pValue->SetNull();
     return;
   }
-  pValue->Assign(pScriptContext->GetJSValueFromMap(pThis));
+  pValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(pThis));
 }
diff --git a/xfa/fxfa/parser/cxfa_document.cpp b/xfa/fxfa/parser/cxfa_document.cpp
index 9200ec2..a30cb54 100644
--- a/xfa/fxfa/parser/cxfa_document.cpp
+++ b/xfa/fxfa/parser/cxfa_document.cpp
@@ -1437,8 +1437,6 @@
   return m_pScriptContext.get();
 }
 
-// We have to call |InitScriptContext| before any calls to |GetScriptContext|
-// or the context won't have an isolate set into it.
 CFXJSE_Engine* CXFA_Document::GetScriptContext() const {
   ASSERT(m_pScriptContext);
   return m_pScriptContext.get();
diff --git a/xfa/fxfa/parser/cxfa_document.h b/xfa/fxfa/parser/cxfa_document.h
index 4a57a177..f8fec25 100644
--- a/xfa/fxfa/parser/cxfa_document.h
+++ b/xfa/fxfa/parser/cxfa_document.h
@@ -59,7 +59,13 @@
   explicit CXFA_Document(CXFA_FFNotify* notify);
   ~CXFA_Document() override;
 
+  bool HasScriptContext() const { return !!m_pScriptContext; }
   CFXJSE_Engine* InitScriptContext(CJS_Runtime* fxjs_runtime);
+
+  // Only safe to call in situations where the context is known to exist,
+  // and always returns non-NULL in those situations. In other words, we have
+  // to call InitScriptContext() first to avoid a situation where the context
+  // won't have an isolate set into it.
   CFXJSE_Engine* GetScriptContext() const;
 
   CXFA_FFNotify* GetNotify() const { return notify_.Get(); }
diff --git a/xfa/fxfa/parser/cxfa_nodeowner.cpp b/xfa/fxfa/parser/cxfa_nodeowner.cpp
index 13c969c..6ee9fd8 100644
--- a/xfa/fxfa/parser/cxfa_nodeowner.cpp
+++ b/xfa/fxfa/parser/cxfa_nodeowner.cpp
@@ -13,7 +13,9 @@
 
 CXFA_NodeOwner::CXFA_NodeOwner() = default;
 
-CXFA_NodeOwner::~CXFA_NodeOwner() = default;
+CXFA_NodeOwner::~CXFA_NodeOwner() {
+  is_being_destroyed_ = true;
+}
 
 CXFA_Node* CXFA_NodeOwner::AddOwnedNode(std::unique_ptr<CXFA_Node> node) {
   if (!node)
diff --git a/xfa/fxfa/parser/cxfa_nodeowner.h b/xfa/fxfa/parser/cxfa_nodeowner.h
index 2aaabe4..e7247cd 100644
--- a/xfa/fxfa/parser/cxfa_nodeowner.h
+++ b/xfa/fxfa/parser/cxfa_nodeowner.h
@@ -18,10 +18,12 @@
 
   CXFA_Node* AddOwnedNode(std::unique_ptr<CXFA_Node> node);
   void FreeOwnedNode(CXFA_Node* node);
+  bool IsBeingDestroyed() const { return is_being_destroyed_; }
 
  protected:
   CXFA_NodeOwner();
 
+  bool is_being_destroyed_ = false;
   std::set<std::unique_ptr<CXFA_Node>> nodes_;
 };
 
diff --git a/xfa/fxfa/parser/cxfa_object.cpp b/xfa/fxfa/parser/cxfa_object.cpp
index a541991..6d730bf 100644
--- a/xfa/fxfa/parser/cxfa_object.cpp
+++ b/xfa/fxfa/parser/cxfa_object.cpp
@@ -30,7 +30,10 @@
       m_elementNameHash(FX_HashCode_GetAsIfW(m_elementName, false)),
       m_pJSObject(std::move(jsObject)) {}
 
-CXFA_Object::~CXFA_Object() = default;
+CXFA_Object::~CXFA_Object() {
+  if (!GetDocument()->IsBeingDestroyed() && GetDocument()->HasScriptContext())
+    GetDocument()->GetScriptContext()->RemoveJSBindingFromMap(this);
+}
 
 CXFA_Object* CXFA_Object::AsCXFAObject() {
   return this;