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