1 // Copyright 2017 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/cjx_tree.h"
8
9 #include "core/fxcrt/numerics/safe_conversions.h"
10 #include "core/fxcrt/span.h"
11 #include "fxjs/fxv8.h"
12 #include "fxjs/js_resources.h"
13 #include "fxjs/xfa/cfxjse_class.h"
14 #include "fxjs/xfa/cfxjse_engine.h"
15 #include "v8/include/cppgc/allocation.h"
16 #include "v8/include/v8-object.h"
17 #include "v8/include/v8-primitive.h"
18 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
19 #include "xfa/fxfa/parser/cxfa_attachnodelist.h"
20 #include "xfa/fxfa/parser/cxfa_document.h"
21 #include "xfa/fxfa/parser/cxfa_node.h"
22 #include "xfa/fxfa/parser/cxfa_object.h"
23
24 const CJX_MethodSpec CJX_Tree::MethodSpecs[] = {
25 {"resolveNode", resolveNode_static},
26 {"resolveNodes", resolveNodes_static}};
27
CJX_Tree(CXFA_Object * obj)28 CJX_Tree::CJX_Tree(CXFA_Object* obj) : CJX_Object(obj) {
29 DefineMethods(MethodSpecs);
30 }
31
32 CJX_Tree::~CJX_Tree() = default;
33
DynamicTypeIs(TypeTag eType) const34 bool CJX_Tree::DynamicTypeIs(TypeTag eType) const {
35 return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
36 }
37
resolveNode(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)38 CJS_Result CJX_Tree::resolveNode(CFXJSE_Engine* runtime,
39 pdfium::span<v8::Local<v8::Value>> params) {
40 if (params.size() != 1)
41 return CJS_Result::Failure(JSMessage::kParamError);
42
43 WideString wsExpression = runtime->ToWideString(params[0]);
44 CXFA_Object* pRefNode = GetXFAObject();
45 if (pRefNode->GetElementType() == XFA_Element::Xfa)
46 pRefNode = runtime->GetThisObject();
47
48 std::optional<CFXJSE_Engine::ResolveResult> maybeResult =
49 runtime->ResolveObjects(
50 ToNode(pRefNode), wsExpression.AsStringView(),
51 Mask<XFA_ResolveFlag>{
52 XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
53 XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
54 XFA_ResolveFlag::kSiblings});
55 if (!maybeResult.has_value())
56 return CJS_Result::Success(runtime->NewNull());
57
58 if (maybeResult.value().type == CFXJSE_Engine::ResolveResult::Type::kNodes) {
59 return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(
60 maybeResult.value().objects.front().Get()));
61 }
62
63 if (!maybeResult.value().script_attribute.callback ||
64 maybeResult.value().script_attribute.eValueType !=
65 XFA_ScriptType::Object) {
66 return CJS_Result::Success(runtime->NewNull());
67 }
68
69 v8::Local<v8::Value> pValue;
70 CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject();
71 (*maybeResult.value().script_attribute.callback)(
72 runtime->GetIsolate(), jsObject, &pValue, false,
73 maybeResult.value().script_attribute.attribute);
74 return CJS_Result::Success(pValue);
75 }
76
resolveNodes(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)77 CJS_Result CJX_Tree::resolveNodes(CFXJSE_Engine* runtime,
78 pdfium::span<v8::Local<v8::Value>> params) {
79 if (params.size() != 1)
80 return CJS_Result::Failure(JSMessage::kParamError);
81
82 CXFA_Object* refNode = GetXFAObject();
83 if (refNode->GetElementType() == XFA_Element::Xfa)
84 refNode = runtime->GetThisObject();
85
86 const Mask<XFA_ResolveFlag> kFlags = {
87 XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
88 XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
89 XFA_ResolveFlag::kSiblings};
90 return CJS_Result::Success(ResolveNodeList(runtime->GetIsolate(),
91 runtime->ToWideString(params[0]),
92 kFlags, ToNode(refNode)));
93 }
94
all(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)95 void CJX_Tree::all(v8::Isolate* pIsolate,
96 v8::Local<v8::Value>* pValue,
97 bool bSetting,
98 XFA_Attribute eAttribute) {
99 if (bSetting) {
100 ThrowInvalidPropertyException(pIsolate);
101 return;
102 }
103 const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kSiblings,
104 XFA_ResolveFlag::kALL};
105 WideString wsExpression = GetAttributeByEnum(XFA_Attribute::Name) + L"[*]";
106 *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr);
107 }
108
classAll(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)109 void CJX_Tree::classAll(v8::Isolate* pIsolate,
110 v8::Local<v8::Value>* pValue,
111 bool bSetting,
112 XFA_Attribute eAttribute) {
113 if (bSetting) {
114 ThrowInvalidPropertyException(pIsolate);
115 return;
116 }
117 const Mask<XFA_ResolveFlag> kFlags = {XFA_ResolveFlag::kSiblings,
118 XFA_ResolveFlag::kALL};
119 WideString wsExpression =
120 L"#" + WideString::FromASCII(GetXFAObject()->GetClassName()) + L"[*]";
121 *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr);
122 }
123
nodes(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)124 void CJX_Tree::nodes(v8::Isolate* pIsolate,
125 v8::Local<v8::Value>* pValue,
126 bool bSetting,
127 XFA_Attribute eAttribute) {
128 if (bSetting) {
129 FXJSE_ThrowMessage(pIsolate, "Unable to set ");
130 return;
131 }
132
133 CXFA_Document* pDoc = GetDocument();
134 auto* pNodeList = cppgc::MakeGarbageCollected<CXFA_AttachNodeList>(
135 pDoc->GetHeap()->GetAllocationHandle(), pDoc, GetXFANode());
136 pDoc->GetNodeOwner()->PersistList(pNodeList);
137
138 CFXJSE_Engine* pEngine = pDoc->GetScriptContext();
139 *pValue = pNodeList->JSObject()->NewBoundV8Object(
140 pIsolate, pEngine->GetJseNormalClass()->GetTemplate(pIsolate));
141 }
142
parent(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)143 void CJX_Tree::parent(v8::Isolate* pIsolate,
144 v8::Local<v8::Value>* pValue,
145 bool bSetting,
146 XFA_Attribute eAttribute) {
147 if (bSetting) {
148 ThrowInvalidPropertyException(pIsolate);
149 return;
150 }
151
152 CXFA_Node* pParent = GetXFANode()->GetParent();
153 *pValue = pParent ? GetDocument()
154 ->GetScriptContext()
155 ->GetOrCreateJSBindingFromMap(pParent)
156 .As<v8::Value>()
157 : fxv8::NewNullHelper(pIsolate).As<v8::Value>();
158 }
159
index(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)160 void CJX_Tree::index(v8::Isolate* pIsolate,
161 v8::Local<v8::Value>* pValue,
162 bool bSetting,
163 XFA_Attribute eAttribute) {
164 if (bSetting) {
165 ThrowInvalidPropertyException(pIsolate);
166 return;
167 }
168
169 CXFA_Node* pNode = GetXFANode();
170 size_t iIndex = pNode ? pNode->GetIndexByName() : 0;
171 *pValue =
172 fxv8::NewNumberHelper(pIsolate, pdfium::checked_cast<int32_t>(iIndex));
173 }
174
classIndex(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)175 void CJX_Tree::classIndex(v8::Isolate* pIsolate,
176 v8::Local<v8::Value>* pValue,
177 bool bSetting,
178 XFA_Attribute eAttribute) {
179 if (bSetting) {
180 ThrowInvalidPropertyException(pIsolate);
181 return;
182 }
183
184 CXFA_Node* pNode = GetXFANode();
185 size_t iIndex = pNode ? pNode->GetIndexByClassName() : 0;
186 *pValue =
187 fxv8::NewNumberHelper(pIsolate, pdfium::checked_cast<int32_t>(iIndex));
188 }
189
somExpression(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)190 void CJX_Tree::somExpression(v8::Isolate* pIsolate,
191 v8::Local<v8::Value>* pValue,
192 bool bSetting,
193 XFA_Attribute eAttribute) {
194 if (bSetting) {
195 ThrowInvalidPropertyException(pIsolate);
196 return;
197 }
198
199 ByteString bsSOMExpression = GetXFAObject()->GetSOMExpression().ToUTF8();
200 *pValue = fxv8::NewStringHelper(pIsolate, bsSOMExpression.AsStringView());
201 }
202
ResolveNodeList(v8::Isolate * pIsolate,WideString wsExpression,Mask<XFA_ResolveFlag> dwFlag,CXFA_Node * refNode)203 v8::Local<v8::Value> CJX_Tree::ResolveNodeList(v8::Isolate* pIsolate,
204 WideString wsExpression,
205 Mask<XFA_ResolveFlag> dwFlag,
206 CXFA_Node* refNode) {
207 if (!refNode)
208 refNode = GetXFANode();
209
210 CXFA_Document* pDoc = GetDocument();
211 auto* pNodeList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
212 pDoc->GetHeap()->GetAllocationHandle(), pDoc);
213 pDoc->GetNodeOwner()->PersistList(pNodeList);
214
215 CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
216 std::optional<CFXJSE_Engine::ResolveResult> maybeResult =
217 pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(),
218 dwFlag);
219
220 if (maybeResult.has_value()) {
221 if (maybeResult.value().type ==
222 CFXJSE_Engine::ResolveResult::Type::kNodes) {
223 for (auto& pObject : maybeResult.value().objects) {
224 if (pObject->IsNode())
225 pNodeList->Append(pObject->AsNode());
226 }
227 } else {
228 if (maybeResult.value().script_attribute.callback &&
229 maybeResult.value().script_attribute.eValueType ==
230 XFA_ScriptType::Object) {
231 for (auto& pObject : maybeResult.value().objects) {
232 v8::Local<v8::Value> innerValue;
233 CJX_Object* jsObject = pObject->JSObject();
234 (*maybeResult.value().script_attribute.callback)(
235 pIsolate, jsObject, &innerValue, false,
236 maybeResult.value().script_attribute.attribute);
237 CXFA_Object* obj =
238 CFXJSE_Engine::ToObject(pScriptContext->GetIsolate(), innerValue);
239 if (obj->IsNode())
240 pNodeList->Append(obj->AsNode());
241 }
242 }
243 }
244 }
245 return pNodeList->JSObject()->NewBoundV8Object(
246 pIsolate, pScriptContext->GetJseNormalClass()->GetTemplate(pIsolate));
247 }
248