| // Copyright 2014 PDFium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
| |
| #include "fxjs/xfa/cfxjse_engine.h" |
| |
| #include <utility> |
| |
| #include "core/fxcrt/autorestorer.h" |
| #include "core/fxcrt/cfx_widetextbuf.h" |
| #include "core/fxcrt/fx_extension.h" |
| #include "fxjs/cjs_runtime.h" |
| #include "fxjs/fxv8.h" |
| #include "fxjs/xfa/cfxjse_class.h" |
| #include "fxjs/xfa/cfxjse_context.h" |
| #include "fxjs/xfa/cfxjse_formcalc_context.h" |
| #include "fxjs/xfa/cfxjse_isolatetracker.h" |
| #include "fxjs/xfa/cfxjse_nodehelper.h" |
| #include "fxjs/xfa/cfxjse_resolveprocessor.h" |
| #include "fxjs/xfa/cfxjse_value.h" |
| #include "fxjs/xfa/cjx_object.h" |
| #include "third_party/base/stl_util.h" |
| #include "xfa/fxfa/cxfa_eventparam.h" |
| #include "xfa/fxfa/cxfa_ffdoc.h" |
| #include "xfa/fxfa/cxfa_ffnotify.h" |
| #include "xfa/fxfa/parser/cxfa_document.h" |
| #include "xfa/fxfa/parser/cxfa_localemgr.h" |
| #include "xfa/fxfa/parser/cxfa_node.h" |
| #include "xfa/fxfa/parser/cxfa_object.h" |
| #include "xfa/fxfa/parser/cxfa_thisproxy.h" |
| #include "xfa/fxfa/parser/xfa_basic_data.h" |
| #include "xfa/fxfa/parser/xfa_utils.h" |
| |
| using pdfium::fxjse::kClassTag; |
| |
| const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor = { |
| kClassTag, // tag |
| "Root", // name |
| nullptr, // methods |
| 0, // method count |
| CFXJSE_Engine::GlobalPropTypeGetter, |
| CFXJSE_Engine::GlobalPropertyGetter, |
| CFXJSE_Engine::GlobalPropertySetter, |
| CFXJSE_Engine::NormalMethodCall, |
| }; |
| |
| const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor = { |
| kClassTag, // tag |
| "XFAObject", // name |
| nullptr, // methods |
| 0, // method count |
| CFXJSE_Engine::NormalPropTypeGetter, |
| CFXJSE_Engine::NormalPropertyGetter, |
| CFXJSE_Engine::NormalPropertySetter, |
| CFXJSE_Engine::NormalMethodCall, |
| }; |
| |
| const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor = { |
| kClassTag, // tag |
| "XFAScriptObject", // name |
| nullptr, // methods |
| 0, // method count |
| CFXJSE_Engine::NormalPropTypeGetter, |
| CFXJSE_Engine::GlobalPropertyGetter, |
| CFXJSE_Engine::GlobalPropertySetter, |
| CFXJSE_Engine::NormalMethodCall, |
| }; |
| |
| namespace { |
| |
| const char kFormCalcRuntime[] = "pfm_rt"; |
| |
| } // namespace |
| |
| CFXJSE_Engine::ResolveResult::ResolveResult() = default; |
| |
| CFXJSE_Engine::ResolveResult::ResolveResult(const ResolveResult& that) = |
| default; |
| |
| CFXJSE_Engine::ResolveResult& CFXJSE_Engine::ResolveResult::operator=( |
| const ResolveResult& that) = default; |
| |
| CFXJSE_Engine::ResolveResult::~ResolveResult() = default; |
| |
| // static |
| CXFA_Object* CFXJSE_Engine::ToObject( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| return ToObject(info.GetIsolate(), info.Holder()); |
| } |
| |
| // static |
| CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate, |
| v8::Local<v8::Value> value) { |
| if (!value->IsObject()) |
| return nullptr; |
| |
| return ToObject(FXJSE_RetrieveObjectBinding(value.As<v8::Object>())); |
| } |
| |
| // static. |
| CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate, |
| CFXJSE_Value* pValue) { |
| return ToObject(pValue->ToHostObject(pIsolate)); |
| } |
| |
| // static |
| CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_HostObject* pHostObj) { |
| if (!pHostObj) |
| return nullptr; |
| |
| CJX_Object* pJSObject = pHostObj->AsCJXObject(); |
| return pJSObject ? pJSObject->GetXFAObject() : nullptr; |
| } |
| |
| CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument, |
| CJS_Runtime* fxjs_runtime) |
| : CFX_V8(fxjs_runtime->GetIsolate()), |
| m_pSubordinateRuntime(fxjs_runtime), |
| m_pDocument(pDocument), |
| m_JsContext(CFXJSE_Context::Create(fxjs_runtime->GetIsolate(), |
| &GlobalClassDescriptor, |
| pDocument->GetRoot()->JSObject(), |
| nullptr)), |
| m_ResolveProcessor(std::make_unique<CFXJSE_ResolveProcessor>()) { |
| RemoveBuiltInObjs(m_JsContext.get()); |
| m_JsContext->EnableCompatibleMode(); |
| |
| // Don't know if this can happen before we remove the builtin objs and set |
| // compatibility mode. |
| m_pJsClass = |
| CFXJSE_Class::Create(m_JsContext.get(), &NormalClassDescriptor, false); |
| } |
| |
| CFXJSE_Engine::~CFXJSE_Engine() { |
| // This is what ensures that the v8 object bound to a CJX_Object |
| // no longer retains that binding since it will outlive that object. |
| CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); |
| for (const auto& pair : m_mapObjectToObject) { |
| const v8::Global<v8::Object>& binding = pair.second; |
| FXJSE_ClearObjectBinding(v8::Local<v8::Object>::New(GetIsolate(), binding)); |
| } |
| } |
| |
| bool CFXJSE_Engine::RunScript(CXFA_Script::Type eScriptType, |
| WideStringView wsScript, |
| CFXJSE_Value* hRetValue, |
| CXFA_Object* pThisObject) { |
| CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); |
| AutoRestorer<CXFA_Script::Type> typeRestorer(&m_eScriptType); |
| m_eScriptType = eScriptType; |
| |
| ByteString btScript; |
| if (eScriptType == CXFA_Script::Type::Formcalc) { |
| if (!m_FM2JSContext) { |
| m_FM2JSContext = std::make_unique<CFXJSE_FormCalcContext>( |
| GetIsolate(), m_JsContext.get(), m_pDocument.Get()); |
| } |
| Optional<CFX_WideTextBuf> wsJavaScript = |
| CFXJSE_FormCalcContext::Translate(m_pDocument->GetHeap(), wsScript); |
| if (!wsJavaScript.has_value()) { |
| hRetValue->SetUndefined(GetIsolate()); |
| return false; |
| } |
| btScript = FX_UTF8Encode(wsJavaScript.value().AsStringView()); |
| } else { |
| btScript = FX_UTF8Encode(wsScript); |
| } |
| AutoRestorer<cppgc::Persistent<CXFA_Object>> nodeRestorer(&m_pThisObject); |
| m_pThisObject = pThisObject; |
| |
| v8::Local<v8::Object> pThisBinding; |
| if (pThisObject) |
| pThisBinding = GetOrCreateJSBindingFromMap(pThisObject); |
| |
| IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime.Get()); |
| return m_JsContext->ExecuteScript(btScript.c_str(), hRetValue, pThisBinding); |
| } |
| |
| bool CFXJSE_Engine::QueryNodeByFlag(CXFA_Node* refNode, |
| WideStringView propname, |
| v8::Local<v8::Value>* pValue, |
| uint32_t dwFlag, |
| bool bSetting) { |
| if (!refNode) |
| return false; |
| |
| Optional<CFXJSE_Engine::ResolveResult> maybeResult = |
| ResolveObjects(refNode, propname, dwFlag); |
| if (!maybeResult.has_value()) |
| return false; |
| |
| if (maybeResult.value().type == ResolveResult::Type::kNodes) { |
| *pValue = |
| GetOrCreateJSBindingFromMap(maybeResult.value().objects.front().Get()); |
| return true; |
| } |
| if (maybeResult.value().type == ResolveResult::Type::kAttribute && |
| maybeResult.value().script_attribute.callback) { |
| CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject(); |
| (*maybeResult.value().script_attribute.callback)( |
| GetIsolate(), jsObject, pValue, bSetting, |
| maybeResult.value().script_attribute.attribute); |
| } |
| return true; |
| } |
| |
| // static |
| void CFXJSE_Engine::GlobalPropertySetter(v8::Isolate* pIsolate, |
| v8::Local<v8::Object> pObject, |
| ByteStringView szPropName, |
| v8::Local<v8::Value> pValue) { |
| CXFA_Object* lpOrginalNode = ToObject(pIsolate, pObject); |
| CXFA_Document* pDoc = lpOrginalNode->GetDocument(); |
| CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext(); |
| CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject()); |
| if (lpOrginalNode->IsThisProxy()) |
| pRefNode = ToNode(lpScriptContext->GetVariablesThis(lpOrginalNode)); |
| |
| WideString wsPropName = WideString::FromUTF8(szPropName); |
| if (lpScriptContext->QueryNodeByFlag( |
| pRefNode, wsPropName.AsStringView(), &pValue, |
| XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings | |
| XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties | |
| XFA_RESOLVENODE_Attributes, |
| true)) { |
| return; |
| } |
| if (lpOrginalNode->IsThisProxy() && fxv8::IsUndefined(pValue)) { |
| fxv8::ReentrantDeleteObjectPropertyHelper(lpScriptContext->GetIsolate(), |
| pObject, szPropName); |
| return; |
| } |
| CXFA_FFNotify* pNotify = pDoc->GetNotify(); |
| if (!pNotify) |
| return; |
| |
| CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); |
| auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime()); |
| if (!pCJSRuntime) |
| return; |
| |
| IJS_Runtime::ScopedEventContext pContext(pCJSRuntime); |
| pCJSRuntime->SetValueByNameInGlobalObject(szPropName, pValue); |
| } |
| |
| // static |
| v8::Local<v8::Value> CFXJSE_Engine::GlobalPropertyGetter( |
| v8::Isolate* pIsolate, |
| v8::Local<v8::Object> pObject, |
| ByteStringView szPropName) { |
| CXFA_Object* pOriginalObject = ToObject(pIsolate, pObject); |
| CXFA_Document* pDoc = pOriginalObject->GetDocument(); |
| CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext(); |
| WideString wsPropName = WideString::FromUTF8(szPropName); |
| |
| // Assume failure. |
| v8::Local<v8::Value> pValue = fxv8::NewUndefinedHelper(pIsolate); |
| |
| if (lpScriptContext->GetType() == CXFA_Script::Type::Formcalc) { |
| if (szPropName == kFormCalcRuntime) |
| return lpScriptContext->m_FM2JSContext->GlobalPropertyGetter(); |
| |
| XFA_HashCode uHashCode = static_cast<XFA_HashCode>( |
| FX_HashCode_GetW(wsPropName.AsStringView(), false)); |
| if (uHashCode != XFA_HASHCODE_Layout) { |
| CXFA_Object* pObj = |
| lpScriptContext->GetDocument()->GetXFAObject(uHashCode); |
| if (pObj) |
| return lpScriptContext->GetOrCreateJSBindingFromMap(pObj); |
| } |
| } |
| |
| CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject()); |
| if (pOriginalObject->IsThisProxy()) { |
| pRefNode = ToNode(lpScriptContext->GetVariablesThis(pOriginalObject)); |
| } |
| if (lpScriptContext->QueryNodeByFlag( |
| pRefNode, wsPropName.AsStringView(), &pValue, |
| XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties | |
| XFA_RESOLVENODE_Attributes, |
| false)) { |
| return pValue; |
| } |
| if (lpScriptContext->QueryNodeByFlag( |
| pRefNode, wsPropName.AsStringView(), &pValue, |
| XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false)) { |
| return pValue; |
| } |
| |
| CXFA_Object* pScriptObject = |
| lpScriptContext->GetVariablesScript(pOriginalObject); |
| if (pScriptObject && |
| lpScriptContext->QueryVariableValue(pScriptObject->AsNode(), szPropName, |
| &pValue, true)) { |
| return pValue; |
| } |
| |
| CXFA_FFNotify* pNotify = pDoc->GetNotify(); |
| if (!pNotify) |
| return pValue; |
| |
| CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); |
| auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime()); |
| if (!pCJSRuntime) |
| return pValue; |
| |
| v8::Local<v8::Value> temp_value; |
| IJS_Runtime::ScopedEventContext pContext(pCJSRuntime); |
| if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_value)) |
| return pValue; |
| |
| return !temp_value.IsEmpty() ? temp_value : pValue; |
| } |
| |
| // static |
| int32_t CFXJSE_Engine::GlobalPropTypeGetter(v8::Isolate* pIsolate, |
| v8::Local<v8::Object> pHolder, |
| ByteStringView szPropName, |
| bool bQueryIn) { |
| CXFA_Object* pObject = ToObject(pIsolate, pHolder); |
| if (!pObject) |
| return FXJSE_ClassPropType_None; |
| |
| CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext(); |
| pObject = lpScriptContext->GetVariablesThis(pObject); |
| WideString wsPropName = WideString::FromUTF8(szPropName); |
| if (pObject->JSObject()->HasMethod(wsPropName)) |
| return FXJSE_ClassPropType_Method; |
| |
| return FXJSE_ClassPropType_Property; |
| } |
| |
| // static |
| v8::Local<v8::Value> CFXJSE_Engine::NormalPropertyGetter( |
| v8::Isolate* pIsolate, |
| v8::Local<v8::Object> pHolder, |
| ByteStringView szPropName) { |
| CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder); |
| if (!pOriginalObject) |
| return fxv8::NewUndefinedHelper(pIsolate); |
| |
| CFXJSE_Engine* lpScriptContext = |
| pOriginalObject->GetDocument()->GetScriptContext(); |
| |
| WideString wsPropName = WideString::FromUTF8(szPropName); |
| if (wsPropName.EqualsASCII("xfa")) { |
| return lpScriptContext->GetOrCreateJSBindingFromMap( |
| lpScriptContext->GetDocument()->GetRoot()); |
| } |
| |
| v8::Local<v8::Value> pReturnValue = fxv8::NewUndefinedHelper(pIsolate); |
| CXFA_Object* pObject = lpScriptContext->GetVariablesThis(pOriginalObject); |
| bool bRet = lpScriptContext->QueryNodeByFlag( |
| ToNode(pObject), wsPropName.AsStringView(), &pReturnValue, |
| XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties | |
| XFA_RESOLVENODE_Attributes, |
| false); |
| if (bRet) |
| return pReturnValue; |
| |
| if (pObject == lpScriptContext->GetThisObject() || |
| (lpScriptContext->GetType() == CXFA_Script::Type::Javascript && |
| !lpScriptContext->IsStrictScopeInJavaScript())) { |
| bRet = lpScriptContext->QueryNodeByFlag( |
| ToNode(pObject), wsPropName.AsStringView(), &pReturnValue, |
| XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false); |
| } |
| if (bRet) |
| return pReturnValue; |
| |
| CXFA_Object* pScriptObject = |
| lpScriptContext->GetVariablesScript(pOriginalObject); |
| if (!pScriptObject) |
| return pReturnValue; |
| |
| bRet = lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName, |
| &pReturnValue, true); |
| if (bRet) |
| return pReturnValue; |
| |
| Optional<XFA_SCRIPTATTRIBUTEINFO> info = XFA_GetScriptAttributeByName( |
| pObject->GetElementType(), wsPropName.AsStringView()); |
| if (info.has_value()) { |
| CJX_Object* jsObject = pObject->JSObject(); |
| (*info.value().callback)(pIsolate, jsObject, &pReturnValue, false, |
| info.value().attribute); |
| return pReturnValue; |
| } |
| |
| CXFA_FFNotify* pNotify = pObject->GetDocument()->GetNotify(); |
| if (!pNotify) |
| return pReturnValue; |
| |
| CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); |
| auto* pCJSRuntime = static_cast<CJS_Runtime*>(hDoc->GetIJSRuntime()); |
| if (!pCJSRuntime) |
| return pReturnValue; |
| |
| IJS_Runtime::ScopedEventContext pContext(pCJSRuntime); |
| v8::Local<v8::Value> temp_local; |
| if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_local)) |
| return pReturnValue; |
| |
| return !temp_local.IsEmpty() ? temp_local : pReturnValue; |
| } |
| |
| // static |
| void CFXJSE_Engine::NormalPropertySetter(v8::Isolate* pIsolate, |
| v8::Local<v8::Object> pHolder, |
| ByteStringView szPropName, |
| v8::Local<v8::Value> pValue) { |
| CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder); |
| if (!pOriginalObject) |
| return; |
| |
| CFXJSE_Engine* lpScriptContext = |
| pOriginalObject->GetDocument()->GetScriptContext(); |
| CXFA_Object* pObject = lpScriptContext->GetVariablesThis(pOriginalObject); |
| WideString wsPropName = WideString::FromUTF8(szPropName); |
| WideStringView wsPropNameView = wsPropName.AsStringView(); |
| Optional<XFA_SCRIPTATTRIBUTEINFO> info = |
| XFA_GetScriptAttributeByName(pObject->GetElementType(), wsPropNameView); |
| if (info.has_value()) { |
| CJX_Object* jsObject = pObject->JSObject(); |
| (*info.value().callback)(pIsolate, jsObject, &pValue, true, |
| info.value().attribute); |
| return; |
| } |
| |
| if (pObject->IsNode()) { |
| if (wsPropNameView[0] == '#') |
| wsPropNameView = wsPropNameView.Last(wsPropNameView.GetLength() - 1); |
| |
| CXFA_Node* pNode = ToNode(pObject); |
| CXFA_Node* pPropOrChild = nullptr; |
| XFA_Element eType = XFA_GetElementByName(wsPropNameView); |
| if (eType != XFA_Element::Unknown) { |
| pPropOrChild = |
| pNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, eType); |
| } else { |
| pPropOrChild = pNode->GetFirstChildByName(wsPropNameView); |
| } |
| |
| if (pPropOrChild) { |
| info = XFA_GetScriptAttributeByName(pPropOrChild->GetElementType(), |
| L"{default}"); |
| if (info.has_value()) { |
| pPropOrChild->JSObject()->ScriptSomDefaultValue(pIsolate, &pValue, true, |
| XFA_Attribute::Unknown); |
| return; |
| } |
| } |
| } |
| |
| CXFA_Object* pScriptObject = |
| lpScriptContext->GetVariablesScript(pOriginalObject); |
| if (pScriptObject) { |
| lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName, |
| &pValue, false); |
| } |
| } |
| |
| int32_t CFXJSE_Engine::NormalPropTypeGetter(v8::Isolate* pIsolate, |
| v8::Local<v8::Object> pHolder, |
| ByteStringView szPropName, |
| bool bQueryIn) { |
| CXFA_Object* pObject = ToObject(pIsolate, pHolder); |
| if (!pObject) |
| return FXJSE_ClassPropType_None; |
| |
| CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext(); |
| pObject = lpScriptContext->GetVariablesThis(pObject); |
| XFA_Element eType = pObject->GetElementType(); |
| WideString wsPropName = WideString::FromUTF8(szPropName); |
| if (pObject->JSObject()->HasMethod(wsPropName)) |
| return FXJSE_ClassPropType_Method; |
| |
| if (bQueryIn && |
| !XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView())) { |
| return FXJSE_ClassPropType_None; |
| } |
| return FXJSE_ClassPropType_Property; |
| } |
| |
| CJS_Result CFXJSE_Engine::NormalMethodCall( |
| const v8::FunctionCallbackInfo<v8::Value>& info, |
| const WideString& functionName) { |
| CXFA_Object* pObject = ToObject(info); |
| if (!pObject) |
| return CJS_Result::Failure(L"no Holder() present."); |
| |
| CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext(); |
| pObject = lpScriptContext->GetVariablesThis(pObject); |
| |
| std::vector<v8::Local<v8::Value>> parameters; |
| for (int i = 0; i < info.Length(); i++) |
| parameters.push_back(info[i]); |
| |
| return pObject->JSObject()->RunMethod(functionName, parameters); |
| } |
| |
| bool CFXJSE_Engine::IsStrictScopeInJavaScript() { |
| return m_pDocument->is_strict_scoping(); |
| } |
| |
| CXFA_Script::Type CFXJSE_Engine::GetType() { |
| return m_eScriptType; |
| } |
| |
| CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Node* pScriptNode, |
| CXFA_Node* pSubform) { |
| if (!pScriptNode || !pSubform) |
| return nullptr; |
| |
| auto* proxy = cppgc::MakeGarbageCollected<CXFA_ThisProxy>( |
| pScriptNode->GetDocument()->GetHeap()->GetAllocationHandle(), pSubform, |
| pScriptNode); |
| auto pNewContext = CFXJSE_Context::Create( |
| GetIsolate(), &VariablesClassDescriptor, proxy->JSObject(), proxy); |
| RemoveBuiltInObjs(pNewContext.get()); |
| pNewContext->EnableCompatibleMode(); |
| CFXJSE_Context* pResult = pNewContext.get(); |
| m_mapVariableToContext[pScriptNode->JSObject()] = std::move(pNewContext); |
| return pResult; |
| } |
| |
| CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject) { |
| CXFA_ThisProxy* pProxy = ToThisProxy(pObject); |
| return pProxy ? pProxy->GetThisNode() : pObject; |
| } |
| |
| CXFA_Object* CFXJSE_Engine::GetVariablesScript(CXFA_Object* pObject) { |
| CXFA_ThisProxy* pProxy = ToThisProxy(pObject); |
| return pProxy ? pProxy->GetScriptNode() : pObject; |
| } |
| |
| bool CFXJSE_Engine::RunVariablesScript(CXFA_Node* pScriptNode) { |
| if (!pScriptNode) |
| return false; |
| |
| if (pScriptNode->GetElementType() != XFA_Element::Script) |
| return true; |
| |
| CXFA_Node* pParent = pScriptNode->GetParent(); |
| if (!pParent || pParent->GetElementType() != XFA_Element::Variables) |
| return false; |
| |
| auto it = m_mapVariableToContext.find(pScriptNode->JSObject()); |
| if (it != m_mapVariableToContext.end() && it->second) |
| return true; |
| |
| CXFA_Node* pTextNode = pScriptNode->GetFirstChild(); |
| if (!pTextNode) |
| return false; |
| |
| Optional<WideString> wsScript = |
| pTextNode->JSObject()->TryCData(XFA_Attribute::Value, true); |
| if (!wsScript) |
| return false; |
| |
| ByteString btScript = wsScript->ToUTF8(); |
| auto hRetValue = std::make_unique<CFXJSE_Value>(); |
| CXFA_Node* pThisObject = pParent->GetParent(); |
| CFXJSE_Context* pVariablesContext = |
| CreateVariablesContext(pScriptNode, pThisObject); |
| AutoRestorer<cppgc::Persistent<CXFA_Object>> nodeRestorer(&m_pThisObject); |
| m_pThisObject = pThisObject; |
| return pVariablesContext->ExecuteScript(btScript.c_str(), hRetValue.get(), |
| v8::Local<v8::Object>()); |
| } |
| |
| bool CFXJSE_Engine::QueryVariableValue(CXFA_Node* pScriptNode, |
| ByteStringView szPropName, |
| v8::Local<v8::Value>* pValue, |
| bool bGetter) { |
| if (!pScriptNode || pScriptNode->GetElementType() != XFA_Element::Script) |
| return false; |
| |
| CXFA_Node* variablesNode = pScriptNode->GetParent(); |
| if (!variablesNode || |
| variablesNode->GetElementType() != XFA_Element::Variables) { |
| return false; |
| } |
| |
| auto it = m_mapVariableToContext.find(pScriptNode->JSObject()); |
| if (it == m_mapVariableToContext.end() || !it->second) |
| return false; |
| |
| CFXJSE_Context* pVariableContext = it->second.get(); |
| v8::Local<v8::Object> pObject = pVariableContext->GetGlobalObject(); |
| if (!bGetter) { |
| fxv8::ReentrantSetObjectOwnPropertyHelper(GetIsolate(), pObject, szPropName, |
| *pValue); |
| return true; |
| } |
| |
| if (!fxv8::ReentrantHasObjectOwnPropertyHelper(GetIsolate(), pObject, |
| szPropName, false)) { |
| return false; |
| } |
| |
| v8::Local<v8::Value> hVariableValue = |
| fxv8::ReentrantGetObjectPropertyHelper(GetIsolate(), pObject, szPropName); |
| if (fxv8::IsFunction(hVariableValue)) { |
| v8::Local<v8::Function> maybeFunc = CFXJSE_Value::NewBoundFunction( |
| GetIsolate(), hVariableValue.As<v8::Function>(), pObject); |
| if (!maybeFunc.IsEmpty()) |
| *pValue = maybeFunc; |
| } else { |
| *pValue = hVariableValue; |
| } |
| return true; |
| } |
| |
| void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) { |
| CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); |
| v8::Local<v8::Object> pObject = pContext->GetGlobalObject(); |
| fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Number"); |
| fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Date"); |
| } |
| |
| Optional<CFXJSE_Engine::ResolveResult> CFXJSE_Engine::ResolveObjects( |
| CXFA_Object* refObject, |
| WideStringView wsExpression, |
| uint32_t dwStyles) { |
| return ResolveObjectsWithBindNode(refObject, wsExpression, dwStyles, nullptr); |
| } |
| |
| Optional<CFXJSE_Engine::ResolveResult> |
| CFXJSE_Engine::ResolveObjectsWithBindNode(CXFA_Object* refObject, |
| WideStringView wsExpression, |
| uint32_t dwStyles, |
| CXFA_Node* bindNode) { |
| if (wsExpression.IsEmpty()) |
| return pdfium::nullopt; |
| |
| ResolveResult result; |
| if (m_eScriptType != CXFA_Script::Type::Formcalc || |
| (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) { |
| m_upObjectArray.clear(); |
| } |
| if (refObject && refObject->IsNode() && |
| (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) { |
| m_upObjectArray.push_back(refObject->AsNode()); |
| } |
| |
| bool bNextCreate = false; |
| CFXJSE_NodeHelper* pNodeHelper = m_ResolveProcessor->GetNodeHelper(); |
| if (dwStyles & XFA_RESOLVENODE_CreateNode) |
| pNodeHelper->SetCreateNodeType(bindNode); |
| |
| pNodeHelper->m_pCreateParent = nullptr; |
| pNodeHelper->m_iCurAllStart = -1; |
| |
| CFXJSE_ResolveNodeData rndFind(this); |
| int32_t nStart = 0; |
| int32_t nLevel = 0; |
| |
| std::vector<cppgc::Member<CXFA_Object>> findObjects; |
| findObjects.emplace_back(refObject ? refObject : m_pDocument->GetRoot()); |
| int32_t nNodes = 0; |
| while (true) { |
| nNodes = pdfium::CollectionSize<int32_t>(findObjects); |
| int32_t i = 0; |
| rndFind.m_dwStyles = dwStyles; |
| m_ResolveProcessor->SetCurStart(nStart); |
| nStart = m_ResolveProcessor->GetFilter(wsExpression, nStart, rndFind); |
| if (nStart < 1) { |
| if ((dwStyles & XFA_RESOLVENODE_CreateNode) && !bNextCreate) { |
| CXFA_Node* pDataNode = nullptr; |
| nStart = pNodeHelper->m_iCurAllStart; |
| if (nStart != -1) { |
| pDataNode = m_pDocument->GetNotBindNode(findObjects); |
| if (pDataNode) { |
| findObjects.clear(); |
| findObjects.emplace_back(pDataNode); |
| break; |
| } |
| } else { |
| pDataNode = findObjects.front()->AsNode(); |
| findObjects.clear(); |
| findObjects.emplace_back(pDataNode); |
| break; |
| } |
| dwStyles |= XFA_RESOLVENODE_Bind; |
| findObjects.clear(); |
| findObjects.emplace_back(pNodeHelper->m_pAllStartParent.Get()); |
| continue; |
| } |
| break; |
| } |
| if (bNextCreate) { |
| int32_t checked_length = |
| pdfium::base::checked_cast<int32_t>(wsExpression.GetLength()); |
| if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition, |
| nStart == checked_length, this)) { |
| continue; |
| } |
| break; |
| } |
| std::vector<cppgc::Member<CXFA_Object>> retObjects; |
| while (i < nNodes) { |
| bool bDataBind = false; |
| if (((dwStyles & XFA_RESOLVENODE_Bind) || |
| (dwStyles & XFA_RESOLVENODE_CreateNode)) && |
| nNodes > 1) { |
| CFXJSE_ResolveNodeData rndBind(nullptr); |
| m_ResolveProcessor->GetFilter(wsExpression, nStart, rndBind); |
| m_ResolveProcessor->SetIndexDataBind(rndBind.m_wsCondition, i, nNodes); |
| bDataBind = true; |
| } |
| rndFind.m_CurObject = findObjects[i++].Get(); |
| rndFind.m_nLevel = nLevel; |
| rndFind.m_Result.type = ResolveResult::Type::kNodes; |
| if (!m_ResolveProcessor->Resolve(GetIsolate(), rndFind)) |
| continue; |
| |
| if (rndFind.m_Result.type == ResolveResult::Type::kAttribute && |
| rndFind.m_Result.script_attribute.callback && |
| nStart < |
| pdfium::base::checked_cast<int32_t>(wsExpression.GetLength())) { |
| v8::Local<v8::Value> pValue; |
| CJX_Object* jsObject = rndFind.m_Result.objects.front()->JSObject(); |
| (*rndFind.m_Result.script_attribute.callback)( |
| GetIsolate(), jsObject, &pValue, false, |
| rndFind.m_Result.script_attribute.attribute); |
| if (!pValue.IsEmpty()) { |
| rndFind.m_Result.objects.front() = ToObject(GetIsolate(), pValue); |
| } |
| } |
| if (!m_upObjectArray.empty()) |
| m_upObjectArray.pop_back(); |
| retObjects.insert(retObjects.end(), rndFind.m_Result.objects.begin(), |
| rndFind.m_Result.objects.end()); |
| rndFind.m_Result.objects.clear(); |
| if (bDataBind) |
| break; |
| } |
| findObjects.clear(); |
| |
| nNodes = pdfium::CollectionSize<int32_t>(retObjects); |
| if (nNodes < 1) { |
| if (dwStyles & XFA_RESOLVENODE_CreateNode) { |
| bNextCreate = true; |
| if (!pNodeHelper->m_pCreateParent) { |
| pNodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject.Get()); |
| pNodeHelper->m_iCreateCount = 1; |
| } |
| int32_t checked_length = |
| pdfium::base::checked_cast<int32_t>(wsExpression.GetLength()); |
| if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition, |
| nStart == checked_length, this)) { |
| continue; |
| } |
| } |
| break; |
| } |
| |
| findObjects = std::move(retObjects); |
| rndFind.m_Result.objects.clear(); |
| if (nLevel == 0) |
| dwStyles &= ~(XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings); |
| |
| nLevel++; |
| } |
| |
| if (!bNextCreate) { |
| result.type = rndFind.m_Result.type; |
| if (nNodes > 0) { |
| result.objects.insert(result.objects.end(), findObjects.begin(), |
| findObjects.end()); |
| } |
| if (rndFind.m_Result.type == ResolveResult::Type::kAttribute) { |
| result.script_attribute = rndFind.m_Result.script_attribute; |
| return result; |
| } |
| } |
| if (dwStyles & (XFA_RESOLVENODE_CreateNode | XFA_RESOLVENODE_Bind | |
| XFA_RESOLVENODE_BindNew)) { |
| if (pNodeHelper->m_pCreateParent) |
| result.objects.emplace_back(pNodeHelper->m_pCreateParent.Get()); |
| else |
| pNodeHelper->CreateNodeForCondition(rndFind.m_wsCondition); |
| |
| result.type = pNodeHelper->m_iCreateFlag; |
| if (result.type == ResolveResult::Type::kCreateNodeOne) { |
| if (pNodeHelper->m_iCurAllStart != -1) |
| result.type = ResolveResult::Type::kCreateNodeMidAll; |
| } |
| |
| if (!bNextCreate && (dwStyles & XFA_RESOLVENODE_CreateNode)) |
| result.type = ResolveResult::Type::kExistNodes; |
| |
| if (result.objects.empty()) |
| return pdfium::nullopt; |
| |
| return result; |
| } |
| if (nNodes == 0) |
| return pdfium::nullopt; |
| |
| return result; |
| } |
| |
| v8::Local<v8::Object> CFXJSE_Engine::GetOrCreateJSBindingFromMap( |
| CXFA_Object* pObject) { |
| if (pObject->IsNode()) |
| RunVariablesScript(pObject->AsNode()); |
| |
| CJX_Object* pCJXObject = pObject->JSObject(); |
| auto iter = m_mapObjectToObject.find(pCJXObject); |
| if (iter != m_mapObjectToObject.end()) |
| return v8::Local<v8::Object>::New(GetIsolate(), iter->second); |
| |
| v8::Local<v8::Object> binding = pCJXObject->NewBoundV8Object( |
| GetIsolate(), m_pJsClass->GetTemplate(GetIsolate())); |
| |
| m_mapObjectToObject[pCJXObject].Reset(GetIsolate(), binding); |
| return binding; |
| } |
| |
| void CFXJSE_Engine::SetNodesOfRunScript( |
| std::vector<cppgc::Persistent<CXFA_Node>>* pArray) { |
| m_pScriptNodeArray = pArray; |
| } |
| |
| void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) { |
| if (m_pScriptNodeArray && !pdfium::Contains(*m_pScriptNodeArray, pNode)) |
| m_pScriptNodeArray->emplace_back(pNode); |
| } |
| |
| CXFA_Object* CFXJSE_Engine::ToXFAObject(v8::Local<v8::Value> obj) { |
| if (!fxv8::IsObject(obj)) |
| return nullptr; |
| |
| CFXJSE_HostObject* pHostObj = |
| FXJSE_RetrieveObjectBinding(obj.As<v8::Object>()); |
| if (!pHostObj) |
| return nullptr; |
| |
| CJX_Object* pJSObject = pHostObj->AsCJXObject(); |
| return pJSObject ? pJSObject->GetXFAObject() : nullptr; |
| } |
| |
| v8::Local<v8::Object> CFXJSE_Engine::NewNormalXFAObject(CXFA_Object* obj) { |
| v8::EscapableHandleScope scope(GetIsolate()); |
| v8::Local<v8::Object> object = obj->JSObject()->NewBoundV8Object( |
| GetIsolate(), GetJseNormalClass()->GetTemplate(GetIsolate())); |
| return scope.Escape(object); |
| } |