1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "fxjs/xfa/cfxjse_engine.h"
8 
9 #include <utility>
10 
11 #include "core/fxcrt/autorestorer.h"
12 #include "core/fxcrt/cfx_widetextbuf.h"
13 #include "core/fxcrt/fx_extension.h"
14 #include "fxjs/cjs_runtime.h"
15 #include "fxjs/xfa/cfxjse_class.h"
16 #include "fxjs/xfa/cfxjse_context.h"
17 #include "fxjs/xfa/cfxjse_formcalc_context.h"
18 #include "fxjs/xfa/cfxjse_resolveprocessor.h"
19 #include "fxjs/xfa/cfxjse_value.h"
20 #include "fxjs/xfa/cjx_object.h"
21 #include "third_party/base/ptr_util.h"
22 #include "third_party/base/stl_util.h"
23 #include "xfa/fxfa/cxfa_eventparam.h"
24 #include "xfa/fxfa/cxfa_ffdoc.h"
25 #include "xfa/fxfa/cxfa_ffnotify.h"
26 #include "xfa/fxfa/parser/cxfa_document.h"
27 #include "xfa/fxfa/parser/cxfa_localemgr.h"
28 #include "xfa/fxfa/parser/cxfa_node.h"
29 #include "xfa/fxfa/parser/cxfa_nodehelper.h"
30 #include "xfa/fxfa/parser/cxfa_object.h"
31 #include "xfa/fxfa/parser/cxfa_thisproxy.h"
32 #include "xfa/fxfa/parser/cxfa_treelist.h"
33 #include "xfa/fxfa/parser/xfa_basic_data.h"
34 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
35 #include "xfa/fxfa/parser/xfa_utils.h"
36 
37 using pdfium::fxjse::kClassTag;
38 
39 const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor = {
40     kClassTag,  // tag
41     "Root",     // name
42     nullptr,    // methods
43     0,          // method count
44     CFXJSE_Engine::GlobalPropTypeGetter,
45     CFXJSE_Engine::GlobalPropertyGetter,
46     CFXJSE_Engine::GlobalPropertySetter,
47     CFXJSE_Engine::NormalMethodCall,
48 };
49 
50 const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor = {
51     kClassTag,    // tag
52     "XFAObject",  // name
53     nullptr,      // methods
54     0,            // method count
55     CFXJSE_Engine::NormalPropTypeGetter,
56     CFXJSE_Engine::NormalPropertyGetter,
57     CFXJSE_Engine::NormalPropertySetter,
58     CFXJSE_Engine::NormalMethodCall,
59 };
60 
61 const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor = {
62     kClassTag,          // tag
63     "XFAScriptObject",  // name
64     nullptr,            // methods
65     0,                  // method count
66     CFXJSE_Engine::NormalPropTypeGetter,
67     CFXJSE_Engine::GlobalPropertyGetter,
68     CFXJSE_Engine::GlobalPropertySetter,
69     CFXJSE_Engine::NormalMethodCall,
70 };
71 
72 namespace {
73 
74 const char kFormCalcRuntime[] = "pfm_rt";
75 
ToThisProxy(CFXJSE_Value * pValue)76 CXFA_ThisProxy* ToThisProxy(CFXJSE_Value* pValue) {
77   CFXJSE_HostObject* pHostObject = pValue->ToHostObject();
78   return pHostObject ? ToThisProxy(pHostObject->AsCXFAObject()) : nullptr;
79 }
80 
81 }  // namespace
82 
83 // static
ToObject(const v8::FunctionCallbackInfo<v8::Value> & info)84 CXFA_Object* CFXJSE_Engine::ToObject(
85     const v8::FunctionCallbackInfo<v8::Value>& info) {
86   if (!info.Holder()->IsObject())
87     return nullptr;
88 
89   CFXJSE_HostObject* pHostObj =
90       FXJSE_RetrieveObjectBinding(info.Holder().As<v8::Object>());
91   return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
92 }
93 
94 // static.
ToObject(CFXJSE_Value * pValue)95 CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_Value* pValue) {
96   CFXJSE_HostObject* pHostObj = pValue->ToHostObject();
97   return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
98 }
99 
CFXJSE_Engine(CXFA_Document * pDocument,CJS_Runtime * fxjs_runtime)100 CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument,
101                              CJS_Runtime* fxjs_runtime)
102     : CFX_V8(fxjs_runtime->GetIsolate()),
103       m_pSubordinateRuntime(fxjs_runtime),
104       m_pDocument(pDocument),
105       m_JsContext(CFXJSE_Context::Create(fxjs_runtime->GetIsolate(),
106                                          &GlobalClassDescriptor,
107                                          pDocument->GetRoot())),
108       m_ResolveProcessor(pdfium::MakeUnique<CFXJSE_ResolveProcessor>()) {
109   RemoveBuiltInObjs(m_JsContext.get());
110   m_JsContext->EnableCompatibleMode();
111 
112   // Don't know if this can happen before we remove the builtin objs and set
113   // compatibility mode.
114   m_pJsClass =
115       CFXJSE_Class::Create(m_JsContext.get(), &NormalClassDescriptor, false);
116 }
117 
~CFXJSE_Engine()118 CFXJSE_Engine::~CFXJSE_Engine() {
119   for (const auto& pair : m_mapVariableToContext)
120     delete ToThisProxy(pair.second->GetGlobalObject().get());
121 
122   for (const auto& pair : m_mapObjectToValue)
123     pair.second->ClearHostObject();
124 }
125 
RunScript(CXFA_Script::Type eScriptType,WideStringView wsScript,CFXJSE_Value * hRetValue,CXFA_Object * pThisObject)126 bool CFXJSE_Engine::RunScript(CXFA_Script::Type eScriptType,
127                               WideStringView wsScript,
128                               CFXJSE_Value* hRetValue,
129                               CXFA_Object* pThisObject) {
130   ByteString btScript;
131   AutoRestorer<CXFA_Script::Type> typeRestorer(&m_eScriptType);
132   m_eScriptType = eScriptType;
133   if (eScriptType == CXFA_Script::Type::Formcalc) {
134     if (!m_FM2JSContext) {
135       m_FM2JSContext = pdfium::MakeUnique<CFXJSE_FormCalcContext>(
136           GetIsolate(), m_JsContext.get(), m_pDocument.Get());
137     }
138     CFX_WideTextBuf wsJavaScript;
139     if (!CFXJSE_FormCalcContext::Translate(wsScript, &wsJavaScript)) {
140       hRetValue->SetUndefined();
141       return false;
142     }
143     btScript = FX_UTF8Encode(wsJavaScript.AsStringView());
144   } else {
145     btScript = FX_UTF8Encode(wsScript);
146   }
147   AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
148   m_pThisObject = pThisObject;
149 
150   CFXJSE_Value* pValue =
151       pThisObject ? GetOrCreateJSBindingFromMap(pThisObject) : nullptr;
152   IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime.Get());
153   return m_JsContext->ExecuteScript(btScript.c_str(), hRetValue, pValue);
154 }
155 
QueryNodeByFlag(CXFA_Node * refNode,WideStringView propname,CFXJSE_Value * pValue,uint32_t dwFlag,bool bSetting)156 bool CFXJSE_Engine::QueryNodeByFlag(CXFA_Node* refNode,
157                                     WideStringView propname,
158                                     CFXJSE_Value* pValue,
159                                     uint32_t dwFlag,
160                                     bool bSetting) {
161   if (!refNode)
162     return false;
163 
164   XFA_RESOLVENODE_RS resolveRs;
165   if (!ResolveObjects(refNode, propname, &resolveRs, dwFlag, nullptr))
166     return false;
167   if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Nodes) {
168     pValue->Assign(
169         GetOrCreateJSBindingFromMap(resolveRs.objects.front().Get()));
170     return true;
171   }
172   if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Attribute &&
173       resolveRs.script_attribute.callback) {
174     CJX_Object* jsObject = resolveRs.objects.front()->JSObject();
175     (*resolveRs.script_attribute.callback)(
176         jsObject, pValue, bSetting, resolveRs.script_attribute.attribute);
177   }
178   return true;
179 }
180 
181 // static
GlobalPropertySetter(CFXJSE_Value * pObject,ByteStringView szPropName,CFXJSE_Value * pValue)182 void CFXJSE_Engine::GlobalPropertySetter(CFXJSE_Value* pObject,
183                                          ByteStringView szPropName,
184                                          CFXJSE_Value* pValue) {
185   CXFA_Object* lpOrginalNode = ToObject(pObject);
186   CXFA_Document* pDoc = lpOrginalNode->GetDocument();
187   CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
188   CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
189   if (lpOrginalNode->IsThisProxy())
190     pRefNode = ToNode(lpScriptContext->GetVariablesThis(lpOrginalNode, false));
191 
192   WideString wsPropName = WideString::FromUTF8(szPropName);
193   if (lpScriptContext->QueryNodeByFlag(
194           pRefNode, wsPropName.AsStringView(), pValue,
195           XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings |
196               XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
197               XFA_RESOLVENODE_Attributes,
198           true)) {
199     return;
200   }
201   if (lpOrginalNode->IsThisProxy() && pValue && pValue->IsUndefined()) {
202     pObject->DeleteObjectProperty(szPropName);
203     return;
204   }
205   CXFA_FFNotify* pNotify = pDoc->GetNotify();
206   if (!pNotify)
207     return;
208 
209   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
210   auto* pCJSRuntime =
211       static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
212   if (!pCJSRuntime)
213     return;
214 
215   v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
216   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
217   pCJSRuntime->SetValueByNameInGlobalObject(
218       szPropName, v8::Local<v8::Value>::New(lpScriptContext->GetIsolate(),
219                                             pValue->DirectGetValue()));
220 }
221 
222 // static
GlobalPropertyGetter(CFXJSE_Value * pObject,ByteStringView szPropName,CFXJSE_Value * pValue)223 void CFXJSE_Engine::GlobalPropertyGetter(CFXJSE_Value* pObject,
224                                          ByteStringView szPropName,
225                                          CFXJSE_Value* pValue) {
226   CXFA_Object* pOriginalObject = ToObject(pObject);
227   CXFA_Document* pDoc = pOriginalObject->GetDocument();
228   CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext();
229   WideString wsPropName = WideString::FromUTF8(szPropName);
230 
231   pValue->SetUndefined();  // Assume failure.
232   if (lpScriptContext->GetType() == CXFA_Script::Type::Formcalc) {
233     if (szPropName == kFormCalcRuntime) {
234       lpScriptContext->m_FM2JSContext->GlobalPropertyGetter(pValue);
235       return;
236     }
237     XFA_HashCode uHashCode = static_cast<XFA_HashCode>(
238         FX_HashCode_GetW(wsPropName.AsStringView(), false));
239     if (uHashCode != XFA_HASHCODE_Layout) {
240       CXFA_Object* pObj =
241           lpScriptContext->GetDocument()->GetXFAObject(uHashCode);
242       if (pObj) {
243         pValue->Assign(lpScriptContext->GetOrCreateJSBindingFromMap(pObj));
244         return;
245       }
246     }
247   }
248 
249   CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject());
250   if (pOriginalObject->IsThisProxy()) {
251     pRefNode =
252         ToNode(lpScriptContext->GetVariablesThis(pOriginalObject, false));
253   }
254   if (lpScriptContext->QueryNodeByFlag(
255           pRefNode, wsPropName.AsStringView(), pValue,
256           XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
257               XFA_RESOLVENODE_Attributes,
258           false)) {
259     return;
260   }
261   if (lpScriptContext->QueryNodeByFlag(
262           pRefNode, wsPropName.AsStringView(), pValue,
263           XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false)) {
264     return;
265   }
266 
267   CXFA_Object* pScriptObject =
268       lpScriptContext->GetVariablesThis(pOriginalObject, true);
269   if (pScriptObject && lpScriptContext->QueryVariableValue(
270                            pScriptObject->AsNode(), szPropName, pValue, true)) {
271     return;
272   }
273 
274   CXFA_FFNotify* pNotify = pDoc->GetNotify();
275   if (!pNotify)
276     return;
277 
278   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
279   auto* pCJSRuntime =
280       static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
281   if (!pCJSRuntime)
282     return;
283 
284   v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
285   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
286   v8::Local<v8::Value> temp_value;
287   if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_value))
288     return;
289 
290   if (temp_value.IsEmpty())
291     return;
292 
293   pValue->ForceSetValue(temp_value);
294 }
295 
GlobalPropTypeGetter(CFXJSE_Value * pOriginalValue,ByteStringView szPropName,bool bQueryIn)296 int32_t CFXJSE_Engine::GlobalPropTypeGetter(CFXJSE_Value* pOriginalValue,
297                                             ByteStringView szPropName,
298                                             bool bQueryIn) {
299   CXFA_Object* pObject = ToObject(pOriginalValue);
300   if (!pObject)
301     return FXJSE_ClassPropType_None;
302 
303   CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
304   pObject = lpScriptContext->GetVariablesThis(pObject, false);
305   WideString wsPropName = WideString::FromUTF8(szPropName);
306   if (pObject->JSObject()->HasMethod(wsPropName))
307     return FXJSE_ClassPropType_Method;
308 
309   return FXJSE_ClassPropType_Property;
310 }
311 
312 // static
NormalPropertyGetter(CFXJSE_Value * pOriginalValue,ByteStringView szPropName,CFXJSE_Value * pReturnValue)313 void CFXJSE_Engine::NormalPropertyGetter(CFXJSE_Value* pOriginalValue,
314                                          ByteStringView szPropName,
315                                          CFXJSE_Value* pReturnValue) {
316   pReturnValue->SetUndefined();  // Assume failure.
317   CXFA_Object* pOriginalObject = ToObject(pOriginalValue);
318   if (!pOriginalObject)
319     return;
320 
321   WideString wsPropName = WideString::FromUTF8(szPropName);
322   CFXJSE_Engine* lpScriptContext =
323       pOriginalObject->GetDocument()->GetScriptContext();
324   CXFA_Object* pObject =
325       lpScriptContext->GetVariablesThis(pOriginalObject, false);
326   if (wsPropName.EqualsASCII("xfa")) {
327     CFXJSE_Value* pValue = lpScriptContext->GetOrCreateJSBindingFromMap(
328         lpScriptContext->GetDocument()->GetRoot());
329     pReturnValue->Assign(pValue);
330     return;
331   }
332 
333   bool bRet = lpScriptContext->QueryNodeByFlag(
334       ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
335       XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
336           XFA_RESOLVENODE_Attributes,
337       false);
338   if (bRet)
339     return;
340 
341   if (pObject == lpScriptContext->GetThisObject() ||
342       (lpScriptContext->GetType() == CXFA_Script::Type::Javascript &&
343        !lpScriptContext->IsStrictScopeInJavaScript())) {
344     bRet = lpScriptContext->QueryNodeByFlag(
345         ToNode(pObject), wsPropName.AsStringView(), pReturnValue,
346         XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false);
347   }
348   if (bRet)
349     return;
350 
351   CXFA_Object* pScriptObject =
352       lpScriptContext->GetVariablesThis(pOriginalObject, true);
353   if (!pScriptObject)
354     return;
355 
356   bRet = lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
357                                              pReturnValue, true);
358   if (bRet)
359     return;
360 
361   Optional<XFA_SCRIPTATTRIBUTEINFO> info = XFA_GetScriptAttributeByName(
362       pObject->GetElementType(), wsPropName.AsStringView());
363   if (info.has_value()) {
364     CJX_Object* jsObject = pObject->JSObject();
365     (*info.value().callback)(jsObject, pReturnValue, false,
366                              info.value().attribute);
367     return;
368   }
369 
370   CXFA_FFNotify* pNotify = pObject->GetDocument()->GetNotify();
371   if (!pNotify)
372     return;
373 
374   CXFA_FFDoc* hDoc = pNotify->GetHDOC();
375   auto* pCJSRuntime =
376       static_cast<CJS_Runtime*>(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc));
377   if (!pCJSRuntime)
378     return;
379 
380   v8::HandleScope handle_scope(lpScriptContext->GetIsolate());
381   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
382   v8::Local<v8::Value> temp_local;
383   if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_local))
384     return;
385 
386   if (temp_local.IsEmpty())
387     return;
388 
389   pReturnValue->ForceSetValue(temp_local);
390 }
391 
392 // static
NormalPropertySetter(CFXJSE_Value * pOriginalValue,ByteStringView szPropName,CFXJSE_Value * pReturnValue)393 void CFXJSE_Engine::NormalPropertySetter(CFXJSE_Value* pOriginalValue,
394                                          ByteStringView szPropName,
395                                          CFXJSE_Value* pReturnValue) {
396   CXFA_Object* pOriginalObject = ToObject(pOriginalValue);
397   if (!pOriginalObject)
398     return;
399 
400   CFXJSE_Engine* lpScriptContext =
401       pOriginalObject->GetDocument()->GetScriptContext();
402   CXFA_Object* pObject =
403       lpScriptContext->GetVariablesThis(pOriginalObject, false);
404   WideString wsPropName = WideString::FromUTF8(szPropName);
405   WideStringView wsPropNameView = wsPropName.AsStringView();
406   Optional<XFA_SCRIPTATTRIBUTEINFO> info =
407       XFA_GetScriptAttributeByName(pObject->GetElementType(), wsPropNameView);
408   if (info.has_value()) {
409     CJX_Object* jsObject = pObject->JSObject();
410     (*info.value().callback)(jsObject, pReturnValue, true,
411                              info.value().attribute);
412     return;
413   }
414 
415   if (pObject->IsNode()) {
416     if (wsPropNameView[0] == '#')
417       wsPropNameView = wsPropNameView.Last(wsPropNameView.GetLength() - 1);
418 
419     CXFA_Node* pNode = ToNode(pObject);
420     CXFA_Node* pPropOrChild = nullptr;
421     XFA_Element eType = XFA_GetElementByName(wsPropNameView);
422     if (eType != XFA_Element::Unknown) {
423       pPropOrChild =
424           pNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, eType);
425     } else {
426       pPropOrChild = pNode->GetFirstChildByName(wsPropNameView);
427     }
428 
429     if (pPropOrChild) {
430       info = XFA_GetScriptAttributeByName(pPropOrChild->GetElementType(),
431                                           L"{default}");
432       if (info.has_value()) {
433         pPropOrChild->JSObject()->ScriptSomDefaultValue(pReturnValue, true,
434                                                         XFA_Attribute::Unknown);
435         return;
436       }
437     }
438   }
439 
440   CXFA_Object* pScriptObject =
441       lpScriptContext->GetVariablesThis(pOriginalObject, true);
442   if (pScriptObject) {
443     lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName,
444                                         pReturnValue, false);
445   }
446 }
447 
NormalPropTypeGetter(CFXJSE_Value * pOriginalValue,ByteStringView szPropName,bool bQueryIn)448 int32_t CFXJSE_Engine::NormalPropTypeGetter(CFXJSE_Value* pOriginalValue,
449                                             ByteStringView szPropName,
450                                             bool bQueryIn) {
451   CXFA_Object* pObject = ToObject(pOriginalValue);
452   if (!pObject)
453     return FXJSE_ClassPropType_None;
454 
455   CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
456   pObject = lpScriptContext->GetVariablesThis(pObject, false);
457   XFA_Element eType = pObject->GetElementType();
458   WideString wsPropName = WideString::FromUTF8(szPropName);
459   if (pObject->JSObject()->HasMethod(wsPropName))
460     return FXJSE_ClassPropType_Method;
461 
462   if (bQueryIn &&
463       !XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView())) {
464     return FXJSE_ClassPropType_None;
465   }
466   return FXJSE_ClassPropType_Property;
467 }
468 
NormalMethodCall(const v8::FunctionCallbackInfo<v8::Value> & info,const WideString & functionName)469 CJS_Result CFXJSE_Engine::NormalMethodCall(
470     const v8::FunctionCallbackInfo<v8::Value>& info,
471     const WideString& functionName) {
472   CXFA_Object* pObject = ToObject(info);
473   if (!pObject)
474     return CJS_Result::Failure(L"no Holder() present.");
475 
476   CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext();
477   pObject = lpScriptContext->GetVariablesThis(pObject, false);
478 
479   std::vector<v8::Local<v8::Value>> parameters;
480   for (int i = 0; i < info.Length(); i++)
481     parameters.push_back(info[i]);
482 
483   return pObject->JSObject()->RunMethod(functionName, parameters);
484 }
485 
IsStrictScopeInJavaScript()486 bool CFXJSE_Engine::IsStrictScopeInJavaScript() {
487   return m_pDocument->is_strict_scoping();
488 }
489 
GetType()490 CXFA_Script::Type CFXJSE_Engine::GetType() {
491   return m_eScriptType;
492 }
493 
CreateVariablesContext(CXFA_Node * pScriptNode,CXFA_Node * pSubform)494 CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Node* pScriptNode,
495                                                       CXFA_Node* pSubform) {
496   if (!pScriptNode || !pSubform)
497     return nullptr;
498 
499   auto pNewContext =
500       CFXJSE_Context::Create(GetIsolate(), &VariablesClassDescriptor,
501                              new CXFA_ThisProxy(pSubform, pScriptNode));
502   RemoveBuiltInObjs(pNewContext.get());
503   pNewContext->EnableCompatibleMode();
504   CFXJSE_Context* pResult = pNewContext.get();
505   m_mapVariableToContext[pScriptNode] = std::move(pNewContext);
506   return pResult;
507 }
508 
GetVariablesThis(CXFA_Object * pObject,bool bScriptNode)509 CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject,
510                                              bool bScriptNode) {
511   CXFA_ThisProxy* pProxy = ToThisProxy(pObject);
512   if (!pProxy)
513     return pObject;
514 
515   return bScriptNode ? pProxy->GetScriptNode() : pProxy->GetThisNode();
516 }
517 
RunVariablesScript(CXFA_Node * pScriptNode)518 bool CFXJSE_Engine::RunVariablesScript(CXFA_Node* pScriptNode) {
519   if (!pScriptNode)
520     return false;
521 
522   if (pScriptNode->GetElementType() != XFA_Element::Script)
523     return true;
524 
525   CXFA_Node* pParent = pScriptNode->GetParent();
526   if (!pParent || pParent->GetElementType() != XFA_Element::Variables)
527     return false;
528 
529   auto it = m_mapVariableToContext.find(pScriptNode);
530   if (it != m_mapVariableToContext.end() && it->second)
531     return true;
532 
533   CXFA_Node* pTextNode = pScriptNode->GetFirstChild();
534   if (!pTextNode)
535     return false;
536 
537   Optional<WideString> wsScript =
538       pTextNode->JSObject()->TryCData(XFA_Attribute::Value, true);
539   if (!wsScript)
540     return false;
541 
542   ByteString btScript = wsScript->ToUTF8();
543   auto hRetValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
544   CXFA_Node* pThisObject = pParent->GetParent();
545   CFXJSE_Context* pVariablesContext =
546       CreateVariablesContext(pScriptNode, pThisObject);
547   AutoRestorer<UnownedPtr<CXFA_Object>> nodeRestorer(&m_pThisObject);
548   m_pThisObject = pThisObject;
549   return pVariablesContext->ExecuteScript(btScript.c_str(), hRetValue.get(),
550                                           nullptr);
551 }
552 
QueryVariableValue(CXFA_Node * pScriptNode,ByteStringView szPropName,CFXJSE_Value * pValue,bool bGetter)553 bool CFXJSE_Engine::QueryVariableValue(CXFA_Node* pScriptNode,
554                                        ByteStringView szPropName,
555                                        CFXJSE_Value* pValue,
556                                        bool bGetter) {
557   if (!pScriptNode || pScriptNode->GetElementType() != XFA_Element::Script)
558     return false;
559 
560   CXFA_Node* variablesNode = pScriptNode->GetParent();
561   if (!variablesNode ||
562       variablesNode->GetElementType() != XFA_Element::Variables)
563     return false;
564 
565   auto it = m_mapVariableToContext.find(pScriptNode);
566   if (it == m_mapVariableToContext.end() || !it->second)
567     return false;
568 
569   CFXJSE_Context* pVariableContext = it->second.get();
570   std::unique_ptr<CFXJSE_Value> pObject = pVariableContext->GetGlobalObject();
571   auto hVariableValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
572   if (!bGetter) {
573     pObject->SetObjectOwnProperty(szPropName, pValue);
574     return true;
575   }
576 
577   if (!pObject->HasObjectOwnProperty(szPropName, false))
578     return false;
579 
580   pObject->GetObjectProperty(szPropName, hVariableValue.get());
581   if (hVariableValue->IsFunction())
582     pValue->SetFunctionBind(hVariableValue.get(), pObject.get());
583   else if (bGetter)
584     pValue->Assign(hVariableValue.get());
585   else
586     hVariableValue.get()->Assign(pValue);
587   return true;
588 }
589 
RemoveBuiltInObjs(CFXJSE_Context * pContext) const590 void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) const {
591   const ByteStringView kObjNames[2] = {"Number", "Date"};
592   std::unique_ptr<CFXJSE_Value> pObject = pContext->GetGlobalObject();
593   auto hProp = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
594   for (const auto& obj : kObjNames) {
595     if (pObject->GetObjectProperty(obj, hProp.get()))
596       pObject->DeleteObjectProperty(obj);
597   }
598 }
599 
ResolveObjects(CXFA_Object * refObject,WideStringView wsExpression,XFA_RESOLVENODE_RS * resolveNodeRS,uint32_t dwStyles,CXFA_Node * bindNode)600 bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject,
601                                    WideStringView wsExpression,
602                                    XFA_RESOLVENODE_RS* resolveNodeRS,
603                                    uint32_t dwStyles,
604                                    CXFA_Node* bindNode) {
605   if (wsExpression.IsEmpty())
606     return false;
607 
608   if (m_eScriptType != CXFA_Script::Type::Formcalc ||
609       (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
610     m_upObjectArray.clear();
611   }
612   if (refObject && refObject->IsNode() &&
613       (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) {
614     m_upObjectArray.push_back(refObject->AsNode());
615   }
616 
617   bool bNextCreate = false;
618   CXFA_NodeHelper* pNodeHelper = m_ResolveProcessor->GetNodeHelper();
619   if (dwStyles & XFA_RESOLVENODE_CreateNode)
620     pNodeHelper->SetCreateNodeType(bindNode);
621 
622   pNodeHelper->m_pCreateParent = nullptr;
623   pNodeHelper->m_iCurAllStart = -1;
624 
625   CFXJSE_ResolveNodeData rndFind(this);
626   int32_t nStart = 0;
627   int32_t nLevel = 0;
628 
629   std::vector<UnownedPtr<CXFA_Object>> findObjects;
630   findObjects.emplace_back(refObject ? refObject : m_pDocument->GetRoot());
631   int32_t nNodes = 0;
632   while (true) {
633     nNodes = pdfium::CollectionSize<int32_t>(findObjects);
634     int32_t i = 0;
635     rndFind.m_dwStyles = dwStyles;
636     m_ResolveProcessor->SetCurStart(nStart);
637     nStart = m_ResolveProcessor->GetFilter(wsExpression, nStart, rndFind);
638     if (nStart < 1) {
639       if ((dwStyles & XFA_RESOLVENODE_CreateNode) && !bNextCreate) {
640         CXFA_Node* pDataNode = nullptr;
641         nStart = pNodeHelper->m_iCurAllStart;
642         if (nStart != -1) {
643           pDataNode = m_pDocument->GetNotBindNode(findObjects);
644           if (pDataNode) {
645             findObjects.clear();
646             findObjects.emplace_back(pDataNode);
647             break;
648           }
649         } else {
650           pDataNode = findObjects.front()->AsNode();
651           findObjects.clear();
652           findObjects.emplace_back(pDataNode);
653           break;
654         }
655         dwStyles |= XFA_RESOLVENODE_Bind;
656         findObjects.clear();
657         findObjects.emplace_back(pNodeHelper->m_pAllStartParent.Get());
658         continue;
659       }
660       break;
661     }
662     if (bNextCreate) {
663       int32_t checked_length =
664           pdfium::base::checked_cast<int32_t>(wsExpression.GetLength());
665       if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
666                                   nStart == checked_length, this)) {
667         continue;
668       }
669       break;
670     }
671     std::vector<UnownedPtr<CXFA_Object>> retObjects;
672     while (i < nNodes) {
673       bool bDataBind = false;
674       if (((dwStyles & XFA_RESOLVENODE_Bind) ||
675            (dwStyles & XFA_RESOLVENODE_CreateNode)) &&
676           nNodes > 1) {
677         CFXJSE_ResolveNodeData rndBind(nullptr);
678         m_ResolveProcessor->GetFilter(wsExpression, nStart, rndBind);
679         m_ResolveProcessor->SetIndexDataBind(rndBind.m_wsCondition, i, nNodes);
680         bDataBind = true;
681       }
682       rndFind.m_CurObject = findObjects[i++].Get();
683       rndFind.m_nLevel = nLevel;
684       rndFind.m_dwFlag = XFA_ResolveNode_RSType_Nodes;
685       if (!m_ResolveProcessor->Resolve(rndFind))
686         continue;
687 
688       if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute &&
689           rndFind.m_ScriptAttribute.callback &&
690           nStart <
691               pdfium::base::checked_cast<int32_t>(wsExpression.GetLength())) {
692         auto pValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
693         CJX_Object* jsObject = rndFind.m_Objects.front()->JSObject();
694         (*rndFind.m_ScriptAttribute.callback)(
695             jsObject, pValue.get(), false, rndFind.m_ScriptAttribute.attribute);
696         if (!pValue->IsEmpty())
697           rndFind.m_Objects.front() = ToObject(pValue.get());
698       }
699       if (!m_upObjectArray.empty())
700         m_upObjectArray.pop_back();
701       retObjects.insert(retObjects.end(), rndFind.m_Objects.begin(),
702                         rndFind.m_Objects.end());
703       rndFind.m_Objects.clear();
704       if (bDataBind)
705         break;
706     }
707     findObjects.clear();
708 
709     nNodes = pdfium::CollectionSize<int32_t>(retObjects);
710     if (nNodes < 1) {
711       if (dwStyles & XFA_RESOLVENODE_CreateNode) {
712         bNextCreate = true;
713         if (!pNodeHelper->m_pCreateParent) {
714           pNodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject.Get());
715           pNodeHelper->m_iCreateCount = 1;
716         }
717         int32_t checked_length =
718             pdfium::base::checked_cast<int32_t>(wsExpression.GetLength());
719         if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
720                                     nStart == checked_length, this)) {
721           continue;
722         }
723       }
724       break;
725     }
726 
727     findObjects = std::move(retObjects);
728     rndFind.m_Objects.clear();
729     if (nLevel == 0)
730       dwStyles &= ~(XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
731 
732     nLevel++;
733   }
734 
735   if (!bNextCreate) {
736     resolveNodeRS->dwFlags = rndFind.m_dwFlag;
737     if (nNodes > 0) {
738       resolveNodeRS->objects.insert(resolveNodeRS->objects.end(),
739                                     findObjects.begin(), findObjects.end());
740     }
741     if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute) {
742       resolveNodeRS->script_attribute = rndFind.m_ScriptAttribute;
743       return true;
744     }
745   }
746   if (dwStyles & (XFA_RESOLVENODE_CreateNode | XFA_RESOLVENODE_Bind |
747                   XFA_RESOLVENODE_BindNew)) {
748     if (pNodeHelper->m_pCreateParent)
749       resolveNodeRS->objects.emplace_back(pNodeHelper->m_pCreateParent.Get());
750     else
751       pNodeHelper->CreateNodeForCondition(rndFind.m_wsCondition);
752 
753     resolveNodeRS->dwFlags = pNodeHelper->m_iCreateFlag;
754     if (resolveNodeRS->dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
755       if (pNodeHelper->m_iCurAllStart != -1)
756         resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_CreateNodeMidAll;
757     }
758 
759     if (!bNextCreate && (dwStyles & XFA_RESOLVENODE_CreateNode))
760       resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_ExistNodes;
761 
762     return !resolveNodeRS->objects.empty();
763   }
764   return nNodes > 0;
765 }
766 
AddToCacheList(std::unique_ptr<CXFA_List> pList)767 void CFXJSE_Engine::AddToCacheList(std::unique_ptr<CXFA_List> pList) {
768   m_CacheList.push_back(std::move(pList));
769 }
770 
GetOrCreateJSBindingFromMap(CXFA_Object * pObject)771 CFXJSE_Value* CFXJSE_Engine::GetOrCreateJSBindingFromMap(CXFA_Object* pObject) {
772   if (pObject->IsNode())
773     RunVariablesScript(pObject->AsNode());
774 
775   auto iter = m_mapObjectToValue.find(pObject);
776   if (iter != m_mapObjectToValue.end())
777     return iter->second.get();
778 
779   auto jsValue = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
780   jsValue->SetHostObject(pObject, m_pJsClass.Get());
781 
782   CFXJSE_Value* pValue = jsValue.get();
783   m_mapObjectToValue.insert(std::make_pair(pObject, std::move(jsValue)));
784   return pValue;
785 }
786 
RemoveJSBindingFromMap(CXFA_Object * pObject)787 void CFXJSE_Engine::RemoveJSBindingFromMap(CXFA_Object* pObject) {
788   auto iter = m_mapObjectToValue.find(pObject);
789   if (iter == m_mapObjectToValue.end())
790     return;
791 
792   iter->second->ClearHostObject();
793   m_mapObjectToValue.erase(iter);
794 }
795 
SetNodesOfRunScript(std::vector<CXFA_Node * > * pArray)796 void CFXJSE_Engine::SetNodesOfRunScript(std::vector<CXFA_Node*>* pArray) {
797   m_pScriptNodeArray = pArray;
798 }
799 
AddNodesOfRunScript(CXFA_Node * pNode)800 void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) {
801   if (m_pScriptNodeArray && !pdfium::ContainsValue(*m_pScriptNodeArray, pNode))
802     m_pScriptNodeArray->push_back(pNode);
803 }
804 
ToXFAObject(v8::Local<v8::Value> obj)805 CXFA_Object* CFXJSE_Engine::ToXFAObject(v8::Local<v8::Value> obj) {
806   if (obj.IsEmpty() || !obj->IsObject())
807     return nullptr;
808 
809   CFXJSE_HostObject* pHostObj =
810       FXJSE_RetrieveObjectBinding(obj.As<v8::Object>());
811   return pHostObj ? pHostObj->AsCXFAObject() : nullptr;
812 }
813 
NewXFAObject(CXFA_Object * obj,v8::Global<v8::FunctionTemplate> & tmpl)814 v8::Local<v8::Value> CFXJSE_Engine::NewXFAObject(
815     CXFA_Object* obj,
816     v8::Global<v8::FunctionTemplate>& tmpl) {
817   v8::EscapableHandleScope scope(GetIsolate());
818   v8::Local<v8::FunctionTemplate> klass =
819       v8::Local<v8::FunctionTemplate>::New(GetIsolate(), tmpl);
820   v8::Local<v8::Object> object = klass->InstanceTemplate()
821                                      ->NewInstance(m_JsContext->GetContext())
822                                      .ToLocalChecked();
823   FXJSE_UpdateObjectBinding(object, obj);
824   return scope.Escape(object);
825 }
826