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