• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The PDFium Authors
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/containers/contains.h"
13 #include "core/fxcrt/fx_extension.h"
14 #include "core/fxcrt/stl_util.h"
15 #include "core/fxcrt/widetext_buffer.h"
16 #include "fxjs/cjs_runtime.h"
17 #include "fxjs/fxv8.h"
18 #include "fxjs/xfa/cfxjse_class.h"
19 #include "fxjs/xfa/cfxjse_context.h"
20 #include "fxjs/xfa/cfxjse_formcalc_context.h"
21 #include "fxjs/xfa/cfxjse_isolatetracker.h"
22 #include "fxjs/xfa/cfxjse_nodehelper.h"
23 #include "fxjs/xfa/cfxjse_resolveprocessor.h"
24 #include "fxjs/xfa/cfxjse_value.h"
25 #include "fxjs/xfa/cjx_object.h"
26 #include "v8/include/v8-function-callback.h"
27 #include "v8/include/v8-function.h"
28 #include "v8/include/v8-local-handle.h"
29 #include "v8/include/v8-object.h"
30 #include "xfa/fxfa/cxfa_eventparam.h"
31 #include "xfa/fxfa/cxfa_ffdoc.h"
32 #include "xfa/fxfa/cxfa_ffnotify.h"
33 #include "xfa/fxfa/parser/cxfa_document.h"
34 #include "xfa/fxfa/parser/cxfa_localemgr.h"
35 #include "xfa/fxfa/parser/cxfa_node.h"
36 #include "xfa/fxfa/parser/cxfa_object.h"
37 #include "xfa/fxfa/parser/cxfa_thisproxy.h"
38 #include "xfa/fxfa/parser/cxfa_variables.h"
39 #include "xfa/fxfa/parser/xfa_basic_data.h"
40 #include "xfa/fxfa/parser/xfa_utils.h"
41 
42 using pdfium::fxjse::kClassTag;
43 
44 const FXJSE_CLASS_DESCRIPTOR kGlobalClassDescriptor = {
45     kClassTag,  // tag
46     "Root",     // name
47     {},         // methods
48     CFXJSE_Engine::GlobalPropTypeGetter,
49     CFXJSE_Engine::GlobalPropertyGetter,
50     CFXJSE_Engine::GlobalPropertySetter,
51     CFXJSE_Engine::NormalMethodCall,
52 };
53 
54 const FXJSE_CLASS_DESCRIPTOR kNormalClassDescriptor = {
55     kClassTag,    // tag
56     "XFAObject",  // name
57     {},           // methods
58     CFXJSE_Engine::NormalPropTypeGetter,
59     CFXJSE_Engine::NormalPropertyGetter,
60     CFXJSE_Engine::NormalPropertySetter,
61     CFXJSE_Engine::NormalMethodCall,
62 };
63 
64 const FXJSE_CLASS_DESCRIPTOR kVariablesClassDescriptor = {
65     kClassTag,          // tag
66     "XFAScriptObject",  // name
67     {},                 // methods
68     CFXJSE_Engine::NormalPropTypeGetter,
69     CFXJSE_Engine::GlobalPropertyGetter,
70     CFXJSE_Engine::GlobalPropertySetter,
71     CFXJSE_Engine::NormalMethodCall,
72 };
73 
74 namespace {
75 
76 const char kFormCalcRuntime[] = "pfm_rt";
77 
78 }  // namespace
79 
80 CFXJSE_Engine::ResolveResult::ResolveResult() = default;
81 
82 CFXJSE_Engine::ResolveResult::ResolveResult(ResolveResult&& that) noexcept =
83     default;
84 
85 CFXJSE_Engine::ResolveResult& CFXJSE_Engine::ResolveResult::operator=(
86     ResolveResult&& that) noexcept = default;
87 
88 CFXJSE_Engine::ResolveResult::~ResolveResult() = default;
89 
90 // static
ToObject(const v8::FunctionCallbackInfo<v8::Value> & info)91 CXFA_Object* CFXJSE_Engine::ToObject(
92     const v8::FunctionCallbackInfo<v8::Value>& info) {
93   return ToObject(info.GetIsolate(), info.This());
94 }
95 
96 // static
ToObject(v8::Isolate * pIsolate,v8::Local<v8::Value> value)97 CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate,
98                                      v8::Local<v8::Value> value) {
99   if (!value->IsObject())
100     return nullptr;
101 
102   return ToObject(FXJSE_RetrieveObjectBinding(value.As<v8::Object>()));
103 }
104 
105 // static.
ToObject(v8::Isolate * pIsolate,CFXJSE_Value * pValue)106 CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate,
107                                      CFXJSE_Value* pValue) {
108   return ToObject(pValue->ToHostObject(pIsolate));
109 }
110 
111 // static
ToObject(CFXJSE_HostObject * pHostObj)112 CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_HostObject* pHostObj) {
113   if (!pHostObj)
114     return nullptr;
115 
116   CJX_Object* pJSObject = pHostObj->AsCJXObject();
117   return pJSObject ? pJSObject->GetXFAObject() : nullptr;
118 }
119 
CFXJSE_Engine(CXFA_Document * pDocument,CJS_Runtime * fxjs_runtime)120 CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument,
121                              CJS_Runtime* fxjs_runtime)
122     : CFX_V8(fxjs_runtime->GetIsolate()),
123       m_pSubordinateRuntime(fxjs_runtime),
124       m_pDocument(pDocument),
125       m_JsContext(CFXJSE_Context::Create(fxjs_runtime->GetIsolate(),
126                                          &kGlobalClassDescriptor,
127                                          pDocument->GetRoot()->JSObject(),
128                                          nullptr)),
129       m_NodeHelper(std::make_unique<CFXJSE_NodeHelper>()),
130       m_ResolveProcessor(
131           std::make_unique<CFXJSE_ResolveProcessor>(this, m_NodeHelper.get())) {
132   RemoveBuiltInObjs(m_JsContext.get());
133   m_JsContext->EnableCompatibleMode();
134 
135   // Don't know if this can happen before we remove the builtin objs and set
136   // compatibility mode.
137   m_pJsClass =
138       CFXJSE_Class::Create(m_JsContext.get(), &kNormalClassDescriptor, false);
139 }
140 
~CFXJSE_Engine()141 CFXJSE_Engine::~CFXJSE_Engine() {
142   // This is what ensures that the v8 object bound to a CJX_Object
143   // no longer retains that binding since it will outlive that object.
144   CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
145   for (const auto& pair : m_mapObjectToObject) {
146     const v8::Global<v8::Object>& binding = pair.second;
147     FXJSE_ClearObjectBinding(v8::Local<v8::Object>::New(GetIsolate(), binding));
148   }
149 }
150 
EventParamScope(CFXJSE_Engine * pEngine,CXFA_Node * pTarget,CXFA_EventParam * pEventParam)151 CFXJSE_Engine::EventParamScope::EventParamScope(CFXJSE_Engine* pEngine,
152                                                 CXFA_Node* pTarget,
153                                                 CXFA_EventParam* pEventParam)
154     : m_pEngine(pEngine),
155       m_pPrevTarget(pEngine->GetEventTarget()),
156       m_pPrevEventParam(pEngine->GetEventParam()) {
157   m_pEngine->m_pTarget = pTarget;
158   m_pEngine->m_eventParam = pEventParam;
159 }
160 
~EventParamScope()161 CFXJSE_Engine::EventParamScope::~EventParamScope() {
162   m_pEngine->m_pTarget = m_pPrevTarget;
163   m_pEngine->m_eventParam = m_pPrevEventParam;
164 }
165 
RunScript(CXFA_Script::Type eScriptType,WideStringView wsScript,CXFA_Object * pThisObject)166 CFXJSE_Context::ExecutionResult CFXJSE_Engine::RunScript(
167     CXFA_Script::Type eScriptType,
168     WideStringView wsScript,
169     CXFA_Object* pThisObject) {
170   CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
171   AutoRestorer<CXFA_Script::Type> typeRestorer(&m_eScriptType);
172   m_eScriptType = eScriptType;
173 
174   ByteString btScript;
175   if (eScriptType == CXFA_Script::Type::Formcalc) {
176     if (!m_FormCalcContext) {
177       m_FormCalcContext = std::make_unique<CFXJSE_FormCalcContext>(
178           GetIsolate(), m_JsContext.get(), m_pDocument.Get());
179     }
180     std::optional<WideTextBuffer> wsJavaScript =
181         CFXJSE_FormCalcContext::Translate(m_pDocument->GetHeap(), wsScript);
182     if (!wsJavaScript.has_value()) {
183       auto undefined_value = std::make_unique<CFXJSE_Value>();
184       undefined_value->SetUndefined(GetIsolate());
185       return CFXJSE_Context::ExecutionResult(false, std::move(undefined_value));
186     }
187     btScript = FX_UTF8Encode(wsJavaScript.value().AsStringView());
188   } else {
189     btScript = FX_UTF8Encode(wsScript);
190   }
191   AutoRestorer<cppgc::Persistent<CXFA_Object>> nodeRestorer(&m_pThisObject);
192   m_pThisObject = pThisObject;
193 
194   v8::Local<v8::Object> pThisBinding;
195   if (pThisObject)
196     pThisBinding = GetOrCreateJSBindingFromMap(pThisObject);
197 
198   IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime);
199   return m_JsContext->ExecuteScript(btScript.AsStringView(), pThisBinding);
200 }
201 
QueryNodeByFlag(CXFA_Node * refNode,WideStringView propname,v8::Local<v8::Value> * pValue,Mask<XFA_ResolveFlag> dwFlag)202 bool CFXJSE_Engine::QueryNodeByFlag(CXFA_Node* refNode,
203                                     WideStringView propname,
204                                     v8::Local<v8::Value>* pValue,
205                                     Mask<XFA_ResolveFlag> dwFlag) {
206   if (!refNode)
207     return false;
208 
209   std::optional<CFXJSE_Engine::ResolveResult> maybeResult =
210       ResolveObjects(refNode, propname, dwFlag);
211   if (!maybeResult.has_value())
212     return false;
213 
214   if (maybeResult.value().type == ResolveResult::Type::kNodes) {
215     *pValue =
216         GetOrCreateJSBindingFromMap(maybeResult.value().objects.front().Get());
217     return true;
218   }
219   if (maybeResult.value().type == ResolveResult::Type::kAttribute &&
220       maybeResult.value().script_attribute.callback) {
221     CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
222     (*maybeResult.value().script_attribute.callback)(
223         GetIsolate(), jsObject, pValue, false,
224         maybeResult.value().script_attribute.attribute);
225   }
226   return true;
227 }
228 
UpdateNodeByFlag(CXFA_Node * refNode,WideStringView propname,v8::Local<v8::Value> pValue,Mask<XFA_ResolveFlag> dwFlag)229 bool CFXJSE_Engine::UpdateNodeByFlag(CXFA_Node* refNode,
230                                      WideStringView propname,
231                                      v8::Local<v8::Value> pValue,
232                                      Mask<XFA_ResolveFlag> dwFlag) {
233   if (!refNode)
234     return false;
235 
236   std::optional<CFXJSE_Engine::ResolveResult> maybeResult =
237       ResolveObjects(refNode, propname, dwFlag);
238   if (!maybeResult.has_value())
239     return false;
240 
241   if (maybeResult.value().type == ResolveResult::Type::kAttribute &&
242       maybeResult.value().script_attribute.callback) {
243     CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
244     (*maybeResult.value().script_attribute.callback)(
245         GetIsolate(), jsObject, &pValue, true,
246         maybeResult.value().script_attribute.attribute);
247   }
248   return true;
249 }
250 
251 // static
GlobalPropertySetter(v8::Isolate * pIsolate,v8::Local<v8::Object> pObject,ByteStringView szPropName,v8::Local<v8::Value> pValue)252 void CFXJSE_Engine::GlobalPropertySetter(v8::Isolate* pIsolate,
253                                          v8::Local<v8::Object> pObject,
254                                          ByteStringView szPropName,
255                                          v8::Local<v8::Value> pValue) {
256   CXFA_Object* pOriginalNode = ToObject(pIsolate, pObject);
257   CXFA_Document* pDoc = pOriginalNode->GetDocument();
258   CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
259   CXFA_Node* pRefNode = ToNode(pScriptContext->GetThisObject());
260   if (pOriginalNode->IsThisProxy())
261     pRefNode = ToNode(pScriptContext->GetVariablesThis(pOriginalNode));
262 
263   WideString wsPropName = WideString::FromUTF8(szPropName);
264   if (pScriptContext->UpdateNodeByFlag(
265           pRefNode, wsPropName.AsStringView(), pValue,
266           Mask<XFA_ResolveFlag>{
267               XFA_ResolveFlag::kParent, XFA_ResolveFlag::kSiblings,
268               XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties,
269               XFA_ResolveFlag::kAttributes})) {
270     return;
271   }
272   if (pOriginalNode->IsThisProxy() && fxv8::IsUndefined(pValue)) {
273     fxv8::ReentrantDeleteObjectPropertyHelper(pScriptContext->GetIsolate(),
274                                               pObject, szPropName);
275     return;
276   }
277   CXFA_FFNotify* pNotify = pDoc->GetNotify();
278   if (!pNotify)
279     return;
280 
281   CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
282   auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
283   if (!pCJSRuntime)
284     return;
285 
286   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
287   pCJSRuntime->SetValueByNameInGlobalObject(szPropName, pValue);
288 }
289 
290 // static
GlobalPropertyGetter(v8::Isolate * pIsolate,v8::Local<v8::Object> pObject,ByteStringView szPropName)291 v8::Local<v8::Value> CFXJSE_Engine::GlobalPropertyGetter(
292     v8::Isolate* pIsolate,
293     v8::Local<v8::Object> pObject,
294     ByteStringView szPropName) {
295   CXFA_Object* pOriginalObject = ToObject(pIsolate, pObject);
296   CXFA_Document* pDoc = pOriginalObject->GetDocument();
297   CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
298   WideString wsPropName = WideString::FromUTF8(szPropName);
299 
300   // Assume failure.
301   v8::Local<v8::Value> pValue = fxv8::NewUndefinedHelper(pIsolate);
302 
303   if (pScriptContext->GetType() == CXFA_Script::Type::Formcalc) {
304     if (szPropName == kFormCalcRuntime)
305       return pScriptContext->m_FormCalcContext->GlobalPropertyGetter();
306 
307     XFA_HashCode uHashCode =
308         static_cast<XFA_HashCode>(FX_HashCode_GetW(wsPropName.AsStringView()));
309     if (uHashCode != XFA_HASHCODE_Layout) {
310       CXFA_Object* pObj =
311           pScriptContext->GetDocument()->GetXFAObject(uHashCode);
312       if (pObj)
313         return pScriptContext->GetOrCreateJSBindingFromMap(pObj);
314     }
315   }
316 
317   CXFA_Node* pRefNode = ToNode(pScriptContext->GetThisObject());
318   if (pOriginalObject->IsThisProxy())
319     pRefNode = ToNode(pScriptContext->GetVariablesThis(pOriginalObject));
320 
321   if (pScriptContext->QueryNodeByFlag(
322           pRefNode, wsPropName.AsStringView(), &pValue,
323           Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
324                                 XFA_ResolveFlag::kProperties,
325                                 XFA_ResolveFlag::kAttributes})) {
326     return pValue;
327   }
328   if (pScriptContext->QueryNodeByFlag(
329           pRefNode, wsPropName.AsStringView(), &pValue,
330           Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
331                                 XFA_ResolveFlag::kSiblings})) {
332     return pValue;
333   }
334 
335   CXFA_Object* pScriptObject =
336       pScriptContext->GetVariablesScript(pOriginalObject);
337   if (pScriptObject && pScriptContext->QueryVariableValue(
338                            CXFA_Script::FromNode(pScriptObject->AsNode()),
339                            szPropName, &pValue)) {
340     return pValue;
341   }
342 
343   CXFA_FFNotify* pNotify = pDoc->GetNotify();
344   if (!pNotify)
345     return pValue;
346 
347   CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
348   auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
349   if (!pCJSRuntime)
350     return pValue;
351 
352   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
353   v8::Local<v8::Value> temp_value =
354       pCJSRuntime->GetValueByNameFromGlobalObject(szPropName);
355 
356   return !temp_value.IsEmpty() ? temp_value : pValue;
357 }
358 
359 // static
GlobalPropTypeGetter(v8::Isolate * pIsolate,v8::Local<v8::Object> pHolder,ByteStringView szPropName,bool bQueryIn)360 FXJSE_ClassPropType CFXJSE_Engine::GlobalPropTypeGetter(
361     v8::Isolate* pIsolate,
362     v8::Local<v8::Object> pHolder,
363     ByteStringView szPropName,
364     bool bQueryIn) {
365   CXFA_Object* pObject = ToObject(pIsolate, pHolder);
366   if (!pObject)
367     return FXJSE_ClassPropType::kNone;
368 
369   CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
370   pObject = pScriptContext->GetVariablesThis(pObject);
371   WideString wsPropName = WideString::FromUTF8(szPropName);
372   if (pObject->JSObject()->HasMethod(wsPropName))
373     return FXJSE_ClassPropType::kMethod;
374 
375   return FXJSE_ClassPropType::kProperty;
376 }
377 
378 // static
NormalPropertyGetter(v8::Isolate * pIsolate,v8::Local<v8::Object> pHolder,ByteStringView szPropName)379 v8::Local<v8::Value> CFXJSE_Engine::NormalPropertyGetter(
380     v8::Isolate* pIsolate,
381     v8::Local<v8::Object> pHolder,
382     ByteStringView szPropName) {
383   CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder);
384   if (!pOriginalObject)
385     return fxv8::NewUndefinedHelper(pIsolate);
386 
387   CFXJSE_Engine* pScriptContext =
388       pOriginalObject->GetDocument()->GetScriptContext();
389 
390   WideString wsPropName = WideString::FromUTF8(szPropName);
391   if (wsPropName.EqualsASCII("xfa")) {
392     return pScriptContext->GetOrCreateJSBindingFromMap(
393         pScriptContext->GetDocument()->GetRoot());
394   }
395 
396   v8::Local<v8::Value> pReturnValue = fxv8::NewUndefinedHelper(pIsolate);
397   CXFA_Object* pObject = pScriptContext->GetVariablesThis(pOriginalObject);
398   CXFA_Node* pRefNode = ToNode(pObject);
399   if (pScriptContext->QueryNodeByFlag(
400           pRefNode, wsPropName.AsStringView(), &pReturnValue,
401           Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kChildren,
402                                 XFA_ResolveFlag::kProperties,
403                                 XFA_ResolveFlag::kAttributes})) {
404     return pReturnValue;
405   }
406   if (pObject == pScriptContext->GetThisObject() ||
407       (pScriptContext->GetType() == CXFA_Script::Type::Javascript &&
408        !pScriptContext->IsStrictScopeInJavaScript())) {
409     if (pScriptContext->QueryNodeByFlag(
410             pRefNode, wsPropName.AsStringView(), &pReturnValue,
411             Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
412                                   XFA_ResolveFlag::kSiblings})) {
413       return pReturnValue;
414     }
415   }
416   CXFA_Object* pScriptObject =
417       pScriptContext->GetVariablesScript(pOriginalObject);
418   if (!pScriptObject)
419     return pReturnValue;
420 
421   if (pScriptContext->QueryVariableValue(
422           CXFA_Script::FromNode(pScriptObject->AsNode()), szPropName,
423           &pReturnValue)) {
424     return pReturnValue;
425   }
426   std::optional<XFA_SCRIPTATTRIBUTEINFO> info = XFA_GetScriptAttributeByName(
427       pObject->GetElementType(), wsPropName.AsStringView());
428   if (info.has_value()) {
429     (*info.value().callback)(pIsolate, pObject->JSObject(), &pReturnValue,
430                              false, info.value().attribute);
431     return pReturnValue;
432   }
433 
434   CXFA_FFNotify* pNotify = pObject->GetDocument()->GetNotify();
435   if (!pNotify)
436     return pReturnValue;
437 
438   CXFA_FFDoc* hDoc = pNotify->GetFFDoc();
439   auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime());
440   if (!pCJSRuntime)
441     return pReturnValue;
442 
443   IJS_Runtime::ScopedEventContext pContext(pCJSRuntime);
444   v8::Local<v8::Value> temp_local =
445       pCJSRuntime->GetValueByNameFromGlobalObject(szPropName);
446 
447   return !temp_local.IsEmpty() ? temp_local : pReturnValue;
448 }
449 
450 // static
NormalPropertySetter(v8::Isolate * pIsolate,v8::Local<v8::Object> pHolder,ByteStringView szPropName,v8::Local<v8::Value> pValue)451 void CFXJSE_Engine::NormalPropertySetter(v8::Isolate* pIsolate,
452                                          v8::Local<v8::Object> pHolder,
453                                          ByteStringView szPropName,
454                                          v8::Local<v8::Value> pValue) {
455   CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder);
456   if (!pOriginalObject)
457     return;
458 
459   CFXJSE_Engine* pScriptContext =
460       pOriginalObject->GetDocument()->GetScriptContext();
461   if (pScriptContext->IsResolvingNodes())
462     return;
463 
464   CXFA_Object* pObject = pScriptContext->GetVariablesThis(pOriginalObject);
465   WideString wsPropName = WideString::FromUTF8(szPropName);
466   WideStringView wsPropNameView = wsPropName.AsStringView();
467   std::optional<XFA_SCRIPTATTRIBUTEINFO> info =
468       XFA_GetScriptAttributeByName(pObject->GetElementType(), wsPropNameView);
469   if (info.has_value()) {
470     CJX_Object* jsObject = pObject->JSObject();
471     (*info.value().callback)(pIsolate, jsObject, &pValue, true,
472                              info.value().attribute);
473     return;
474   }
475 
476   if (pObject->IsNode()) {
477     if (wsPropNameView[0] == '#')
478       wsPropNameView = wsPropNameView.Last(wsPropNameView.GetLength() - 1);
479 
480     CXFA_Node* pNode = ToNode(pObject);
481     CXFA_Node* pPropOrChild = nullptr;
482     XFA_Element eType = XFA_GetElementByName(wsPropNameView);
483     if (eType != XFA_Element::Unknown) {
484       pPropOrChild =
485           pNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, eType);
486     } else {
487       pPropOrChild = pNode->GetFirstChildByName(wsPropNameView);
488     }
489 
490     if (pPropOrChild) {
491       info = XFA_GetScriptAttributeByName(pPropOrChild->GetElementType(),
492                                           L"{default}");
493       if (info.has_value()) {
494         pPropOrChild->JSObject()->ScriptSomDefaultValue(pIsolate, &pValue, true,
495                                                         XFA_Attribute::Unknown);
496         return;
497       }
498     }
499   }
500 
501   CXFA_Object* pScriptObject =
502       pScriptContext->GetVariablesScript(pOriginalObject);
503   if (pScriptObject) {
504     pScriptContext->UpdateVariableValue(
505         CXFA_Script::FromNode(pScriptObject->AsNode()), szPropName, pValue);
506   }
507 }
508 
NormalPropTypeGetter(v8::Isolate * pIsolate,v8::Local<v8::Object> pHolder,ByteStringView szPropName,bool bQueryIn)509 FXJSE_ClassPropType CFXJSE_Engine::NormalPropTypeGetter(
510     v8::Isolate* pIsolate,
511     v8::Local<v8::Object> pHolder,
512     ByteStringView szPropName,
513     bool bQueryIn) {
514   CXFA_Object* pObject = ToObject(pIsolate, pHolder);
515   if (!pObject)
516     return FXJSE_ClassPropType::kNone;
517 
518   CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
519   pObject = pScriptContext->GetVariablesThis(pObject);
520   XFA_Element eType = pObject->GetElementType();
521   WideString wsPropName = WideString::FromUTF8(szPropName);
522   if (pObject->JSObject()->HasMethod(wsPropName))
523     return FXJSE_ClassPropType::kMethod;
524 
525   if (bQueryIn) {
526     std::optional<XFA_SCRIPTATTRIBUTEINFO> maybe_info =
527         XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView());
528     if (!maybe_info.has_value())
529       return FXJSE_ClassPropType::kNone;
530   }
531   return FXJSE_ClassPropType::kProperty;
532 }
533 
NormalMethodCall(const v8::FunctionCallbackInfo<v8::Value> & info,const WideString & functionName)534 CJS_Result CFXJSE_Engine::NormalMethodCall(
535     const v8::FunctionCallbackInfo<v8::Value>& info,
536     const WideString& functionName) {
537   CXFA_Object* pObject = ToObject(info);
538   if (!pObject) {
539     return CJS_Result::Failure(WideString::FromASCII("no Holder() present."));
540   }
541   CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext();
542   pObject = pScriptContext->GetVariablesThis(pObject);
543 
544   v8::LocalVector<v8::Value> parameters(info.GetIsolate());
545   for (int i = 0; i < info.Length(); i++) {
546     parameters.push_back(info[i]);
547   }
548   return pObject->JSObject()->RunMethod(pScriptContext, functionName,
549                                         parameters);
550 }
551 
IsStrictScopeInJavaScript()552 bool CFXJSE_Engine::IsStrictScopeInJavaScript() {
553   return m_pDocument->is_strict_scoping();
554 }
555 
GetType()556 CXFA_Script::Type CFXJSE_Engine::GetType() {
557   return m_eScriptType;
558 }
559 
AddObjectToUpArray(CXFA_Node * pNode)560 void CFXJSE_Engine::AddObjectToUpArray(CXFA_Node* pNode) {
561   m_upObjectArray.push_back(pNode);
562 }
563 
LastObjectFromUpArray()564 CXFA_Node* CFXJSE_Engine::LastObjectFromUpArray() {
565   return !m_upObjectArray.empty() ? m_upObjectArray.back() : nullptr;
566 }
567 
CreateVariablesContext(CXFA_Script * pScriptNode,CXFA_Node * pSubform)568 CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Script* pScriptNode,
569                                                       CXFA_Node* pSubform) {
570   if (!pScriptNode || !pSubform)
571     return nullptr;
572 
573   auto* proxy = cppgc::MakeGarbageCollected<CXFA_ThisProxy>(
574       pScriptNode->GetDocument()->GetHeap()->GetAllocationHandle(), pSubform,
575       pScriptNode);
576   auto pNewContext = CFXJSE_Context::Create(
577       GetIsolate(), &kVariablesClassDescriptor, proxy->JSObject(), proxy);
578   RemoveBuiltInObjs(pNewContext.get());
579   pNewContext->EnableCompatibleMode();
580   CFXJSE_Context* pResult = pNewContext.get();
581   m_mapVariableToContext[pScriptNode->JSObject()] = std::move(pNewContext);
582   return pResult;
583 }
584 
GetVariablesThis(CXFA_Object * pObject)585 CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject) {
586   CXFA_ThisProxy* pProxy = ToThisProxy(pObject);
587   return pProxy ? pProxy->GetThisNode() : pObject;
588 }
589 
GetVariablesScript(CXFA_Object * pObject)590 CXFA_Object* CFXJSE_Engine::GetVariablesScript(CXFA_Object* pObject) {
591   CXFA_ThisProxy* pProxy = ToThisProxy(pObject);
592   return pProxy ? pProxy->GetScriptNode() : pObject;
593 }
594 
RunVariablesScript(CXFA_Script * pScriptNode)595 void CFXJSE_Engine::RunVariablesScript(CXFA_Script* pScriptNode) {
596   if (!pScriptNode)
597     return;
598 
599   auto* pParent = CXFA_Variables::FromNode(pScriptNode->GetParent());
600   if (!pParent)
601     return;
602 
603   auto it = m_mapVariableToContext.find(pScriptNode->JSObject());
604   if (it != m_mapVariableToContext.end() && it->second)
605     return;
606 
607   CXFA_Node* pTextNode = pScriptNode->GetFirstChild();
608   if (!pTextNode)
609     return;
610 
611   std::optional<WideString> wsScript =
612       pTextNode->JSObject()->TryCData(XFA_Attribute::Value, true);
613   if (!wsScript.has_value())
614     return;
615 
616   ByteString btScript = wsScript->ToUTF8();
617   CXFA_Node* pThisObject = pParent->GetParent();
618   CFXJSE_Context* pVariablesContext =
619       CreateVariablesContext(pScriptNode, pThisObject);
620   AutoRestorer<cppgc::Persistent<CXFA_Object>> nodeRestorer(&m_pThisObject);
621   m_pThisObject = pThisObject;
622   pVariablesContext->ExecuteScript(btScript.AsStringView(),
623                                    v8::Local<v8::Object>());
624 }
625 
VariablesContextForScriptNode(CXFA_Script * pScriptNode)626 CFXJSE_Context* CFXJSE_Engine::VariablesContextForScriptNode(
627     CXFA_Script* pScriptNode) {
628   if (!pScriptNode)
629     return nullptr;
630 
631   auto* variablesNode = CXFA_Variables::FromNode(pScriptNode->GetParent());
632   if (!variablesNode)
633     return nullptr;
634 
635   auto it = m_mapVariableToContext.find(pScriptNode->JSObject());
636   return it != m_mapVariableToContext.end() ? it->second.get() : nullptr;
637 }
638 
QueryVariableValue(CXFA_Script * pScriptNode,ByteStringView szPropName,v8::Local<v8::Value> * pValue)639 bool CFXJSE_Engine::QueryVariableValue(CXFA_Script* pScriptNode,
640                                        ByteStringView szPropName,
641                                        v8::Local<v8::Value>* pValue) {
642   CFXJSE_Context* pVariableContext = VariablesContextForScriptNode(pScriptNode);
643   if (!pVariableContext)
644     return false;
645 
646   v8::Local<v8::Object> pObject = pVariableContext->GetGlobalObject();
647   if (!fxv8::ReentrantHasObjectOwnPropertyHelper(GetIsolate(), pObject,
648                                                  szPropName)) {
649     return false;
650   }
651 
652   v8::Local<v8::Value> hVariableValue =
653       fxv8::ReentrantGetObjectPropertyHelper(GetIsolate(), pObject, szPropName);
654   if (fxv8::IsFunction(hVariableValue)) {
655     v8::Local<v8::Function> maybeFunc = CFXJSE_Value::NewBoundFunction(
656         GetIsolate(), hVariableValue.As<v8::Function>(), pObject);
657     if (!maybeFunc.IsEmpty())
658       *pValue = maybeFunc;
659   } else {
660     *pValue = hVariableValue;
661   }
662   return true;
663 }
664 
UpdateVariableValue(CXFA_Script * pScriptNode,ByteStringView szPropName,v8::Local<v8::Value> pValue)665 bool CFXJSE_Engine::UpdateVariableValue(CXFA_Script* pScriptNode,
666                                         ByteStringView szPropName,
667                                         v8::Local<v8::Value> pValue) {
668   CFXJSE_Context* pVariableContext = VariablesContextForScriptNode(pScriptNode);
669   if (!pVariableContext)
670     return false;
671 
672   v8::Local<v8::Object> pObject = pVariableContext->GetGlobalObject();
673   fxv8::ReentrantSetObjectOwnPropertyHelper(GetIsolate(), pObject, szPropName,
674                                             pValue);
675   return true;
676 }
677 
RemoveBuiltInObjs(CFXJSE_Context * pContext)678 void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) {
679   CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
680   v8::Local<v8::Object> pObject = pContext->GetGlobalObject();
681   fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Number");
682   fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Date");
683 }
684 
ResolveObjects(CXFA_Object * refObject,WideStringView wsExpression,Mask<XFA_ResolveFlag> dwStyles)685 std::optional<CFXJSE_Engine::ResolveResult> CFXJSE_Engine::ResolveObjects(
686     CXFA_Object* refObject,
687     WideStringView wsExpression,
688     Mask<XFA_ResolveFlag> dwStyles) {
689   return ResolveObjectsWithBindNode(refObject, wsExpression, dwStyles, nullptr);
690 }
691 
692 std::optional<CFXJSE_Engine::ResolveResult>
ResolveObjectsWithBindNode(CXFA_Object * refObject,WideStringView wsExpression,Mask<XFA_ResolveFlag> dwStyles,CXFA_Node * bindNode)693 CFXJSE_Engine::ResolveObjectsWithBindNode(CXFA_Object* refObject,
694                                           WideStringView wsExpression,
695                                           Mask<XFA_ResolveFlag> dwStyles,
696                                           CXFA_Node* bindNode) {
697   if (wsExpression.IsEmpty())
698     return std::nullopt;
699 
700   AutoRestorer<bool> resolving_restorer(&m_bResolvingNodes);
701   m_bResolvingNodes = true;
702 
703   const bool bParentOrSiblings =
704       !!(dwStyles & Mask<XFA_ResolveFlag>{XFA_ResolveFlag::kParent,
705                                           XFA_ResolveFlag::kSiblings});
706   if (m_eScriptType != CXFA_Script::Type::Formcalc || bParentOrSiblings)
707     m_upObjectArray.clear();
708   if (refObject && refObject->IsNode() && bParentOrSiblings)
709     m_upObjectArray.push_back(refObject->AsNode());
710 
711   ResolveResult result;
712   bool bNextCreate = false;
713   if (dwStyles & XFA_ResolveFlag::kCreateNode)
714     m_NodeHelper->SetCreateNodeType(bindNode);
715 
716   m_NodeHelper->m_pCreateParent = nullptr;
717   m_NodeHelper->m_iCurAllStart = -1;
718 
719   CFXJSE_ResolveProcessor::NodeData rndFind;
720   int32_t nStart = 0;
721   int32_t nLevel = 0;
722 
723   std::vector<cppgc::Member<CXFA_Object>> findObjects;
724   findObjects.emplace_back(refObject ? refObject : m_pDocument->GetRoot());
725   int32_t nNodes = 0;
726   CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext());
727   while (true) {
728     nNodes = fxcrt::CollectionSize<int32_t>(findObjects);
729     int32_t i = 0;
730     rndFind.m_dwStyles = dwStyles;
731     m_ResolveProcessor->SetCurStart(nStart);
732     nStart = m_ResolveProcessor->GetFilter(wsExpression, nStart, rndFind);
733     if (nStart < 1) {
734       if ((dwStyles & XFA_ResolveFlag::kCreateNode) && !bNextCreate) {
735         CXFA_Node* pDataNode = nullptr;
736         nStart = m_NodeHelper->m_iCurAllStart;
737         if (nStart != -1) {
738           pDataNode = m_pDocument->GetNotBindNode(findObjects);
739           if (pDataNode) {
740             findObjects.clear();
741             findObjects.emplace_back(pDataNode);
742             break;
743           }
744         } else {
745           pDataNode = findObjects.front()->AsNode();
746           findObjects.clear();
747           findObjects.emplace_back(pDataNode);
748           break;
749         }
750         dwStyles |= XFA_ResolveFlag::kBind;
751         findObjects.clear();
752         findObjects.emplace_back(m_NodeHelper->m_pAllStartParent.Get());
753         continue;
754       }
755       break;
756     }
757     if (bNextCreate) {
758       int32_t checked_length =
759           pdfium::checked_cast<int32_t>(wsExpression.GetLength());
760       if (m_NodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
761                                    nStart == checked_length, this)) {
762         continue;
763       }
764       break;
765     }
766     std::vector<cppgc::Member<CXFA_Object>> retObjects;
767     while (i < nNodes) {
768       bool bDataBind = false;
769       if (((dwStyles & XFA_ResolveFlag::kBind) ||
770            (dwStyles & XFA_ResolveFlag::kCreateNode)) &&
771           nNodes > 1) {
772         CFXJSE_ResolveProcessor::NodeData rndBind;
773         m_ResolveProcessor->GetFilter(wsExpression, nStart, rndBind);
774         i = m_ResolveProcessor->IndexForDataBind(rndBind.m_wsCondition, nNodes);
775         bDataBind = true;
776       }
777       rndFind.m_CurObject = findObjects[i++].Get();
778       rndFind.m_nLevel = nLevel;
779       rndFind.m_Result.type = ResolveResult::Type::kNodes;
780       if (!m_ResolveProcessor->Resolve(GetIsolate(), rndFind))
781         continue;
782 
783       if (rndFind.m_Result.type == ResolveResult::Type::kAttribute &&
784           rndFind.m_Result.script_attribute.callback &&
785           nStart < pdfium::checked_cast<int32_t>(wsExpression.GetLength())) {
786         v8::Local<v8::Value> pValue;
787         CJX_Object* jsObject = rndFind.m_Result.objects.front()->JSObject();
788         (*rndFind.m_Result.script_attribute.callback)(
789             GetIsolate(), jsObject, &pValue, false,
790             rndFind.m_Result.script_attribute.attribute);
791         if (!pValue.IsEmpty()) {
792           rndFind.m_Result.objects.front() = ToObject(GetIsolate(), pValue);
793         }
794       }
795       if (!m_upObjectArray.empty())
796         m_upObjectArray.pop_back();
797       retObjects.insert(retObjects.end(), rndFind.m_Result.objects.begin(),
798                         rndFind.m_Result.objects.end());
799       rndFind.m_Result.objects.clear();
800       if (bDataBind)
801         break;
802     }
803     findObjects.clear();
804 
805     nNodes = fxcrt::CollectionSize<int32_t>(retObjects);
806     if (nNodes < 1) {
807       if (dwStyles & XFA_ResolveFlag::kCreateNode) {
808         bNextCreate = true;
809         if (!m_NodeHelper->m_pCreateParent) {
810           m_NodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject);
811           m_NodeHelper->m_iCreateCount = 1;
812         }
813         int32_t checked_length =
814             pdfium::checked_cast<int32_t>(wsExpression.GetLength());
815         if (m_NodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition,
816                                      nStart == checked_length, this)) {
817           continue;
818         }
819       }
820       break;
821     }
822 
823     findObjects = std::move(retObjects);
824     rndFind.m_Result.objects.clear();
825     if (nLevel == 0) {
826       dwStyles.Clear(XFA_ResolveFlag::kParent);
827       dwStyles.Clear(XFA_ResolveFlag::kSiblings);
828     }
829     nLevel++;
830   }
831 
832   if (!bNextCreate) {
833     result.type = rndFind.m_Result.type;
834     if (nNodes > 0) {
835       result.objects.insert(result.objects.end(), findObjects.begin(),
836                             findObjects.end());
837     }
838     if (rndFind.m_Result.type == ResolveResult::Type::kAttribute) {
839       result.script_attribute = rndFind.m_Result.script_attribute;
840       return result;
841     }
842   }
843   if ((dwStyles & XFA_ResolveFlag::kCreateNode) ||
844       (dwStyles & XFA_ResolveFlag::kBind) ||
845       (dwStyles & XFA_ResolveFlag::kBindNew)) {
846     if (m_NodeHelper->m_pCreateParent)
847       result.objects.emplace_back(m_NodeHelper->m_pCreateParent.Get());
848     else
849       m_NodeHelper->CreateNodeForCondition(rndFind.m_wsCondition);
850 
851     result.type = m_NodeHelper->m_iCreateFlag;
852     if (result.type == ResolveResult::Type::kCreateNodeOne) {
853       if (m_NodeHelper->m_iCurAllStart != -1)
854         result.type = ResolveResult::Type::kCreateNodeMidAll;
855     }
856 
857     if (!bNextCreate && (dwStyles & XFA_ResolveFlag::kCreateNode))
858       result.type = ResolveResult::Type::kExistNodes;
859 
860     if (result.objects.empty())
861       return std::nullopt;
862 
863     return result;
864   }
865   if (nNodes == 0)
866     return std::nullopt;
867 
868   return result;
869 }
870 
GetOrCreateJSBindingFromMap(CXFA_Object * pObject)871 v8::Local<v8::Object> CFXJSE_Engine::GetOrCreateJSBindingFromMap(
872     CXFA_Object* pObject) {
873   RunVariablesScript(CXFA_Script::FromNode(pObject->AsNode()));
874 
875   CJX_Object* pCJXObject = pObject->JSObject();
876   auto iter = m_mapObjectToObject.find(pCJXObject);
877   if (iter != m_mapObjectToObject.end())
878     return v8::Local<v8::Object>::New(GetIsolate(), iter->second);
879 
880   v8::Local<v8::Object> binding = pCJXObject->NewBoundV8Object(
881       GetIsolate(), m_pJsClass->GetTemplate(GetIsolate()));
882 
883   m_mapObjectToObject[pCJXObject].Reset(GetIsolate(), binding);
884   return binding;
885 }
886 
SetNodesOfRunScript(std::vector<cppgc::Persistent<CXFA_Node>> * pArray)887 void CFXJSE_Engine::SetNodesOfRunScript(
888     std::vector<cppgc::Persistent<CXFA_Node>>* pArray) {
889   m_pScriptNodeArray = pArray;
890 }
891 
AddNodesOfRunScript(CXFA_Node * pNode)892 void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) {
893   if (m_pScriptNodeArray && !pdfium::Contains(*m_pScriptNodeArray, pNode))
894     m_pScriptNodeArray->emplace_back(pNode);
895 }
896 
ToXFAObject(v8::Local<v8::Value> obj)897 CXFA_Object* CFXJSE_Engine::ToXFAObject(v8::Local<v8::Value> obj) {
898   if (!fxv8::IsObject(obj))
899     return nullptr;
900 
901   CFXJSE_HostObject* pHostObj =
902       FXJSE_RetrieveObjectBinding(obj.As<v8::Object>());
903   if (!pHostObj)
904     return nullptr;
905 
906   CJX_Object* pJSObject = pHostObj->AsCJXObject();
907   return pJSObject ? pJSObject->GetXFAObject() : nullptr;
908 }
909 
NewNormalXFAObject(CXFA_Object * obj)910 v8::Local<v8::Object> CFXJSE_Engine::NewNormalXFAObject(CXFA_Object* obj) {
911   v8::EscapableHandleScope scope(GetIsolate());
912   v8::Local<v8::Object> object = obj->JSObject()->NewBoundV8Object(
913       GetIsolate(), GetJseNormalClass()->GetTemplate(GetIsolate()));
914   return scope.Escape(object);
915 }
916