| // Copyright 2017 The PDFium Authors | 
 | // 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/cjx_tree.h" | 
 |  | 
 | #include <vector> | 
 |  | 
 | #include "fxjs/fxv8.h" | 
 | #include "fxjs/js_resources.h" | 
 | #include "fxjs/xfa/cfxjse_class.h" | 
 | #include "fxjs/xfa/cfxjse_engine.h" | 
 | #include "third_party/base/numerics/safe_conversions.h" | 
 | #include "v8/include/cppgc/allocation.h" | 
 | #include "v8/include/v8-object.h" | 
 | #include "v8/include/v8-primitive.h" | 
 | #include "xfa/fxfa/parser/cxfa_arraynodelist.h" | 
 | #include "xfa/fxfa/parser/cxfa_attachnodelist.h" | 
 | #include "xfa/fxfa/parser/cxfa_document.h" | 
 | #include "xfa/fxfa/parser/cxfa_node.h" | 
 | #include "xfa/fxfa/parser/cxfa_object.h" | 
 |  | 
 | const CJX_MethodSpec CJX_Tree::MethodSpecs[] = { | 
 |     {"resolveNode", resolveNode_static}, | 
 |     {"resolveNodes", resolveNodes_static}}; | 
 |  | 
 | CJX_Tree::CJX_Tree(CXFA_Object* obj) : CJX_Object(obj) { | 
 |   DefineMethods(MethodSpecs); | 
 | } | 
 |  | 
 | CJX_Tree::~CJX_Tree() = default; | 
 |  | 
 | bool CJX_Tree::DynamicTypeIs(TypeTag eType) const { | 
 |   return eType == static_type__ || ParentType__::DynamicTypeIs(eType); | 
 | } | 
 |  | 
 | CJS_Result CJX_Tree::resolveNode( | 
 |     CFXJSE_Engine* runtime, | 
 |     const std::vector<v8::Local<v8::Value>>& params) { | 
 |   if (params.size() != 1) | 
 |     return CJS_Result::Failure(JSMessage::kParamError); | 
 |  | 
 |   WideString wsExpression = runtime->ToWideString(params[0]); | 
 |   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); | 
 |   CXFA_Object* pRefNode = GetXFAObject(); | 
 |   if (pRefNode->GetElementType() == XFA_Element::Xfa) | 
 |     pRefNode = pScriptContext->GetThisObject(); | 
 |  | 
 |   absl::optional<CFXJSE_Engine::ResolveResult> maybeResult = | 
 |       pScriptContext->ResolveObjects( | 
 |           ToNode(pRefNode), wsExpression.AsStringView(), | 
 |           Mask<XFA_ResolveFlag>{ | 
 |               XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes, | 
 |               XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent, | 
 |               XFA_ResolveFlag::kSiblings}); | 
 |   if (!maybeResult.has_value()) | 
 |     return CJS_Result::Success(runtime->NewNull()); | 
 |  | 
 |   if (maybeResult.value().type == CFXJSE_Engine::ResolveResult::Type::kNodes) { | 
 |     return CJS_Result::Success( | 
 |         GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( | 
 |             maybeResult.value().objects.front().Get())); | 
 |   } | 
 |  | 
 |   if (!maybeResult.value().script_attribute.callback || | 
 |       maybeResult.value().script_attribute.eValueType != | 
 |           XFA_ScriptType::Object) { | 
 |     return CJS_Result::Success(runtime->NewNull()); | 
 |   } | 
 |  | 
 |   v8::Local<v8::Value> pValue; | 
 |   CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject(); | 
 |   (*maybeResult.value().script_attribute.callback)( | 
 |       runtime->GetIsolate(), jsObject, &pValue, false, | 
 |       maybeResult.value().script_attribute.attribute); | 
 |   return CJS_Result::Success(pValue); | 
 | } | 
 |  | 
 | CJS_Result CJX_Tree::resolveNodes( | 
 |     CFXJSE_Engine* runtime, | 
 |     const std::vector<v8::Local<v8::Value>>& params) { | 
 |   if (params.size() != 1) | 
 |     return CJS_Result::Failure(JSMessage::kParamError); | 
 |  | 
 |   CXFA_Object* refNode = GetXFAObject(); | 
 |   if (refNode->GetElementType() == XFA_Element::Xfa) | 
 |     refNode = GetDocument()->GetScriptContext()->GetThisObject(); | 
 |  | 
 |   CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); | 
 |   const Mask<XFA_ResolveFlag> kFlags = { | 
 |       XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes, | 
 |       XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent, | 
 |       XFA_ResolveFlag::kSiblings}; | 
 |   return CJS_Result::Success(ResolveNodeList(pScriptContext->GetIsolate(), | 
 |                                              runtime->ToWideString(params[0]), | 
 |                                              kFlags, ToNode(refNode))); | 
 | } | 
 |  | 
 | void CJX_Tree::all(v8::Isolate* pIsolate, | 
 |                    v8::Local<v8::Value>* pValue, | 
 |                    bool bSetting, | 
 |                    XFA_Attribute eAttribute) { | 
 |   if (bSetting) { | 
 |     ThrowInvalidPropertyException(pIsolate); | 
 |     return; | 
 |   } | 
 |   const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kSiblings, | 
 |                                         XFA_ResolveFlag::kALL}; | 
 |   WideString wsExpression = GetAttributeByEnum(XFA_Attribute::Name) + L"[*]"; | 
 |   *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr); | 
 | } | 
 |  | 
 | void CJX_Tree::classAll(v8::Isolate* pIsolate, | 
 |                         v8::Local<v8::Value>* pValue, | 
 |                         bool bSetting, | 
 |                         XFA_Attribute eAttribute) { | 
 |   if (bSetting) { | 
 |     ThrowInvalidPropertyException(pIsolate); | 
 |     return; | 
 |   } | 
 |   const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kSiblings, | 
 |                                         XFA_ResolveFlag::kALL}; | 
 |   WideString wsExpression = | 
 |       L"#" + WideString::FromASCII(GetXFAObject()->GetClassName()) + L"[*]"; | 
 |   *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr); | 
 | } | 
 |  | 
 | void CJX_Tree::nodes(v8::Isolate* pIsolate, | 
 |                      v8::Local<v8::Value>* pValue, | 
 |                      bool bSetting, | 
 |                      XFA_Attribute eAttribute) { | 
 |   if (bSetting) { | 
 |     WideString wsMessage = L"Unable to set "; | 
 |     FXJSE_ThrowMessage(pIsolate, wsMessage.ToUTF8().AsStringView()); | 
 |     return; | 
 |   } | 
 |  | 
 |   CXFA_Document* pDoc = GetDocument(); | 
 |   auto* pNodeList = cppgc::MakeGarbageCollected<CXFA_AttachNodeList>( | 
 |       pDoc->GetHeap()->GetAllocationHandle(), pDoc, GetXFANode()); | 
 |   pDoc->GetNodeOwner()->PersistList(pNodeList); | 
 |  | 
 |   CFXJSE_Engine* pEngine = pDoc->GetScriptContext(); | 
 |   *pValue = pNodeList->JSObject()->NewBoundV8Object( | 
 |       pIsolate, pEngine->GetJseNormalClass()->GetTemplate(pIsolate)); | 
 | } | 
 |  | 
 | void CJX_Tree::parent(v8::Isolate* pIsolate, | 
 |                       v8::Local<v8::Value>* pValue, | 
 |                       bool bSetting, | 
 |                       XFA_Attribute eAttribute) { | 
 |   if (bSetting) { | 
 |     ThrowInvalidPropertyException(pIsolate); | 
 |     return; | 
 |   } | 
 |  | 
 |   CXFA_Node* pParent = GetXFANode()->GetParent(); | 
 |   *pValue = pParent ? GetDocument() | 
 |                           ->GetScriptContext() | 
 |                           ->GetOrCreateJSBindingFromMap(pParent) | 
 |                           .As<v8::Value>() | 
 |                     : fxv8::NewNullHelper(pIsolate).As<v8::Value>(); | 
 | } | 
 |  | 
 | void CJX_Tree::index(v8::Isolate* pIsolate, | 
 |                      v8::Local<v8::Value>* pValue, | 
 |                      bool bSetting, | 
 |                      XFA_Attribute eAttribute) { | 
 |   if (bSetting) { | 
 |     ThrowInvalidPropertyException(pIsolate); | 
 |     return; | 
 |   } | 
 |  | 
 |   CXFA_Node* pNode = GetXFANode(); | 
 |   size_t iIndex = pNode ? pNode->GetIndexByName() : 0; | 
 |   *pValue = fxv8::NewNumberHelper(pIsolate, | 
 |                                   pdfium::base::checked_cast<int32_t>(iIndex)); | 
 | } | 
 |  | 
 | void CJX_Tree::classIndex(v8::Isolate* pIsolate, | 
 |                           v8::Local<v8::Value>* pValue, | 
 |                           bool bSetting, | 
 |                           XFA_Attribute eAttribute) { | 
 |   if (bSetting) { | 
 |     ThrowInvalidPropertyException(pIsolate); | 
 |     return; | 
 |   } | 
 |  | 
 |   CXFA_Node* pNode = GetXFANode(); | 
 |   size_t iIndex = pNode ? pNode->GetIndexByClassName() : 0; | 
 |   *pValue = fxv8::NewNumberHelper(pIsolate, | 
 |                                   pdfium::base::checked_cast<int32_t>(iIndex)); | 
 | } | 
 |  | 
 | void CJX_Tree::somExpression(v8::Isolate* pIsolate, | 
 |                              v8::Local<v8::Value>* pValue, | 
 |                              bool bSetting, | 
 |                              XFA_Attribute eAttribute) { | 
 |   if (bSetting) { | 
 |     ThrowInvalidPropertyException(pIsolate); | 
 |     return; | 
 |   } | 
 |  | 
 |   ByteString bsSOMExpression = GetXFAObject()->GetSOMExpression().ToUTF8(); | 
 |   *pValue = fxv8::NewStringHelper(pIsolate, bsSOMExpression.AsStringView()); | 
 | } | 
 |  | 
 | v8::Local<v8::Value> CJX_Tree::ResolveNodeList(v8::Isolate* pIsolate, | 
 |                                                WideString wsExpression, | 
 |                                                Mask<XFA_ResolveFlag> dwFlag, | 
 |                                                CXFA_Node* refNode) { | 
 |   if (!refNode) | 
 |     refNode = GetXFANode(); | 
 |  | 
 |   CXFA_Document* pDoc = GetDocument(); | 
 |   auto* pNodeList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>( | 
 |       pDoc->GetHeap()->GetAllocationHandle(), pDoc); | 
 |   pDoc->GetNodeOwner()->PersistList(pNodeList); | 
 |  | 
 |   CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); | 
 |   absl::optional<CFXJSE_Engine::ResolveResult> maybeResult = | 
 |       pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(), | 
 |                                      dwFlag); | 
 |  | 
 |   if (maybeResult.has_value()) { | 
 |     if (maybeResult.value().type == | 
 |         CFXJSE_Engine::ResolveResult::Type::kNodes) { | 
 |       for (auto& pObject : maybeResult.value().objects) { | 
 |         if (pObject->IsNode()) | 
 |           pNodeList->Append(pObject->AsNode()); | 
 |       } | 
 |     } else { | 
 |       if (maybeResult.value().script_attribute.callback && | 
 |           maybeResult.value().script_attribute.eValueType == | 
 |               XFA_ScriptType::Object) { | 
 |         for (auto& pObject : maybeResult.value().objects) { | 
 |           v8::Local<v8::Value> innerValue; | 
 |           CJX_Object* jsObject = pObject->JSObject(); | 
 |           (*maybeResult.value().script_attribute.callback)( | 
 |               pIsolate, jsObject, &innerValue, false, | 
 |               maybeResult.value().script_attribute.attribute); | 
 |           CXFA_Object* obj = | 
 |               CFXJSE_Engine::ToObject(pScriptContext->GetIsolate(), innerValue); | 
 |           if (obj->IsNode()) | 
 |             pNodeList->Append(obj->AsNode()); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |   return pNodeList->JSObject()->NewBoundV8Object( | 
 |       pIsolate, pScriptContext->GetJseNormalClass()->GetTemplate(pIsolate)); | 
 | } |