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_instancemanager.h"
8
9 #include <algorithm>
10 #include <vector>
11
12 #include "fxjs/fxv8.h"
13 #include "fxjs/js_resources.h"
14 #include "fxjs/xfa/cfxjse_engine.h"
15 #include "third_party/base/notreached.h"
16 #include "v8/include/v8-object.h"
17 #include "v8/include/v8-primitive.h"
18 #include "xfa/fxfa/cxfa_ffdoc.h"
19 #include "xfa/fxfa/cxfa_ffnotify.h"
20 #include "xfa/fxfa/parser/cxfa_document.h"
21 #include "xfa/fxfa/parser/cxfa_instancemanager.h"
22 #include "xfa/fxfa/parser/cxfa_occur.h"
23 #include "xfa/fxfa/parser/cxfa_subform.h"
24
25 const CJX_MethodSpec CJX_InstanceManager::MethodSpecs[] = {
26 {"addInstance", addInstance_static},
27 {"insertInstance", insertInstance_static},
28 {"moveInstance", moveInstance_static},
29 {"removeInstance", removeInstance_static},
30 {"setInstances", setInstances_static}};
31
CJX_InstanceManager(CXFA_InstanceManager * mgr)32 CJX_InstanceManager::CJX_InstanceManager(CXFA_InstanceManager* mgr)
33 : CJX_Node(mgr) {
34 DefineMethods(MethodSpecs);
35 }
36
37 CJX_InstanceManager::~CJX_InstanceManager() = default;
38
DynamicTypeIs(TypeTag eType) const39 bool CJX_InstanceManager::DynamicTypeIs(TypeTag eType) const {
40 return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
41 }
42
SetInstances(v8::Isolate * pIsolate,int32_t iDesired)43 int32_t CJX_InstanceManager::SetInstances(v8::Isolate* pIsolate,
44 int32_t iDesired) {
45 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
46 int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
47 if (iDesired < iMin) {
48 ThrowTooManyOccurrencesException(pIsolate, L"min");
49 return 1;
50 }
51
52 int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
53 if (iMax >= 0 && iDesired > iMax) {
54 ThrowTooManyOccurrencesException(pIsolate, L"max");
55 return 2;
56 }
57
58 int32_t iCount = GetXFANode()->GetCount();
59 if (iDesired == iCount)
60 return 0;
61
62 if (iDesired < iCount) {
63 WideString wsInstManagerName = GetCData(XFA_Attribute::Name);
64 WideString wsInstanceName = WideString(
65 wsInstManagerName.IsEmpty()
66 ? wsInstManagerName
67 : wsInstManagerName.Last(wsInstManagerName.GetLength() - 1));
68 uint32_t dInstanceNameHash =
69 FX_HashCode_GetW(wsInstanceName.AsStringView());
70 CXFA_Node* pPrevSibling = iDesired == 0
71 ? GetXFANode()
72 : GetXFANode()->GetItemIfExists(iDesired - 1);
73 if (!pPrevSibling) {
74 // TODO(dsinclair): Better error?
75 ThrowIndexOutOfBoundsException(pIsolate);
76 return 0;
77 }
78
79 while (iCount > iDesired) {
80 CXFA_Node* pRemoveInstance = pPrevSibling->GetNextSibling();
81 if (pRemoveInstance->GetElementType() != XFA_Element::Subform &&
82 pRemoveInstance->GetElementType() != XFA_Element::SubformSet) {
83 continue;
84 }
85 if (pRemoveInstance->GetElementType() == XFA_Element::InstanceManager) {
86 NOTREACHED();
87 break;
88 }
89 if (pRemoveInstance->GetNameHash() == dInstanceNameHash) {
90 GetXFANode()->RemoveItem(pRemoveInstance, true);
91 iCount--;
92 }
93 }
94 } else {
95 while (iCount < iDesired) {
96 CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(true);
97 if (!pNewInstance)
98 return 0;
99
100 GetXFANode()->InsertItem(pNewInstance, iCount, iCount, false);
101 ++iCount;
102
103 CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
104 if (!pNotify)
105 return 0;
106
107 pNotify->RunNodeInitialize(pNewInstance);
108 }
109 }
110 GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
111 return 0;
112 }
113
MoveInstance(v8::Isolate * pIsolate,int32_t iTo,int32_t iFrom)114 int32_t CJX_InstanceManager::MoveInstance(v8::Isolate* pIsolate,
115 int32_t iTo,
116 int32_t iFrom) {
117 int32_t iCount = GetXFANode()->GetCount();
118 if (iFrom > iCount || iTo > iCount - 1) {
119 ThrowIndexOutOfBoundsException(pIsolate);
120 return 1;
121 }
122 if (iFrom < 0 || iTo < 0 || iFrom == iTo)
123 return 0;
124
125 CXFA_Node* pMoveInstance = GetXFANode()->GetItemIfExists(iFrom);
126 if (!pMoveInstance) {
127 ThrowIndexOutOfBoundsException(pIsolate);
128 return 1;
129 }
130
131 GetXFANode()->RemoveItem(pMoveInstance, false);
132 GetXFANode()->InsertItem(pMoveInstance, iTo, iCount - 1, true);
133 GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
134 return 0;
135 }
136
moveInstance(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)137 CJS_Result CJX_InstanceManager::moveInstance(
138 CFXJSE_Engine* runtime,
139 const std::vector<v8::Local<v8::Value>>& params) {
140 CXFA_Document* doc = runtime->GetDocument();
141 if (doc->GetFormType() != FormType::kXFAFull)
142 return CJS_Result::Failure(JSMessage::kNotSupportedError);
143
144 if (params.size() != 2)
145 return CJS_Result::Failure(JSMessage::kParamError);
146
147 int32_t iFrom = runtime->ToInt32(params[0]);
148 int32_t iTo = runtime->ToInt32(params[1]);
149 MoveInstance(runtime->GetIsolate(), iTo, iFrom);
150
151 CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
152 if (!pNotify)
153 return CJS_Result::Success();
154
155 CXFA_Node* pXFA = GetXFANode();
156 auto* pToInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iTo));
157 if (pToInstance)
158 pNotify->RunSubformIndexChange(pToInstance);
159
160 auto* pFromInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iFrom));
161 if (pFromInstance)
162 pNotify->RunSubformIndexChange(pFromInstance);
163
164 return CJS_Result::Success();
165 }
166
removeInstance(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)167 CJS_Result CJX_InstanceManager::removeInstance(
168 CFXJSE_Engine* runtime,
169 const std::vector<v8::Local<v8::Value>>& params) {
170 CXFA_Document* doc = runtime->GetDocument();
171 if (doc->GetFormType() != FormType::kXFAFull)
172 return CJS_Result::Failure(JSMessage::kNotSupportedError);
173
174 if (params.size() != 1)
175 return CJS_Result::Failure(JSMessage::kParamError);
176
177 int32_t iIndex = runtime->ToInt32(params[0]);
178 int32_t iCount = GetXFANode()->GetCount();
179 if (iIndex < 0 || iIndex >= iCount)
180 return CJS_Result::Failure(JSMessage::kInvalidInputError);
181
182 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
183 int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
184 if (iCount - 1 < iMin)
185 return CJS_Result::Failure(JSMessage::kTooManyOccurrences);
186
187 CXFA_Node* pRemoveInstance = GetXFANode()->GetItemIfExists(iIndex);
188 if (!pRemoveInstance)
189 return CJS_Result::Failure(JSMessage::kParamError);
190
191 GetXFANode()->RemoveItem(pRemoveInstance, true);
192
193 CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
194 if (pNotify) {
195 CXFA_Node* pXFA = GetXFANode();
196 for (int32_t i = iIndex; i < iCount - 1; i++) {
197 auto* pSubformInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(i));
198 if (pSubformInstance)
199 pNotify->RunSubformIndexChange(pSubformInstance);
200 }
201 }
202 GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
203 return CJS_Result::Success();
204 }
205
setInstances(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)206 CJS_Result CJX_InstanceManager::setInstances(
207 CFXJSE_Engine* runtime,
208 const std::vector<v8::Local<v8::Value>>& params) {
209 CXFA_Document* doc = runtime->GetDocument();
210 if (doc->GetFormType() != FormType::kXFAFull)
211 return CJS_Result::Failure(JSMessage::kNotSupportedError);
212
213 if (params.size() != 1)
214 return CJS_Result::Failure(JSMessage::kParamError);
215
216 SetInstances(runtime->GetIsolate(), runtime->ToInt32(params[0]));
217 return CJS_Result::Success();
218 }
219
addInstance(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)220 CJS_Result CJX_InstanceManager::addInstance(
221 CFXJSE_Engine* runtime,
222 const std::vector<v8::Local<v8::Value>>& params) {
223 CXFA_Document* doc = runtime->GetDocument();
224 if (doc->GetFormType() != FormType::kXFAFull)
225 return CJS_Result::Failure(JSMessage::kNotSupportedError);
226
227 if (!params.empty() && params.size() != 1)
228 return CJS_Result::Failure(JSMessage::kParamError);
229
230 bool fFlags = true;
231 if (params.size() == 1)
232 fFlags = runtime->ToBoolean(params[0]);
233
234 int32_t iCount = GetXFANode()->GetCount();
235 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
236 int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
237 if (iMax >= 0 && iCount >= iMax)
238 return CJS_Result::Failure(JSMessage::kTooManyOccurrences);
239
240 CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(fFlags);
241 if (!pNewInstance)
242 return CJS_Result::Success(runtime->NewNull());
243
244 GetXFANode()->InsertItem(pNewInstance, iCount, iCount, false);
245
246 CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
247 if (pNotify) {
248 pNotify->RunNodeInitialize(pNewInstance);
249 GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
250 }
251
252 return CJS_Result::Success(
253 GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
254 pNewInstance));
255 }
256
insertInstance(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)257 CJS_Result CJX_InstanceManager::insertInstance(
258 CFXJSE_Engine* runtime,
259 const std::vector<v8::Local<v8::Value>>& params) {
260 CXFA_Document* doc = runtime->GetDocument();
261 if (doc->GetFormType() != FormType::kXFAFull)
262 return CJS_Result::Failure(JSMessage::kNotSupportedError);
263
264 if (params.size() != 1 && params.size() != 2)
265 return CJS_Result::Failure(JSMessage::kParamError);
266
267 int32_t iIndex = runtime->ToInt32(params[0]);
268 bool bBind = false;
269 if (params.size() == 2)
270 bBind = runtime->ToBoolean(params[1]);
271
272 int32_t iCount = GetXFANode()->GetCount();
273 if (iIndex < 0 || iIndex > iCount)
274 return CJS_Result::Failure(JSMessage::kInvalidInputError);
275
276 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
277 int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
278 if (iMax >= 0 && iCount >= iMax)
279 return CJS_Result::Failure(JSMessage::kInvalidInputError);
280
281 CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(bBind);
282 if (!pNewInstance)
283 return CJS_Result::Success(runtime->NewNull());
284
285 GetXFANode()->InsertItem(pNewInstance, iIndex, iCount, true);
286
287 CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
288 if (pNotify) {
289 pNotify->RunNodeInitialize(pNewInstance);
290 GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
291 }
292
293 return CJS_Result::Success(
294 GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
295 pNewInstance));
296 }
297
max(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)298 void CJX_InstanceManager::max(v8::Isolate* pIsolate,
299 v8::Local<v8::Value>* pValue,
300 bool bSetting,
301 XFA_Attribute eAttribute) {
302 if (bSetting) {
303 ThrowInvalidPropertyException(pIsolate);
304 return;
305 }
306 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
307 *pValue = fxv8::NewNumberHelper(
308 pIsolate, occur ? occur->GetMax() : CXFA_Occur::kDefaultMax);
309 }
310
min(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)311 void CJX_InstanceManager::min(v8::Isolate* pIsolate,
312 v8::Local<v8::Value>* pValue,
313 bool bSetting,
314 XFA_Attribute eAttribute) {
315 if (bSetting) {
316 ThrowInvalidPropertyException(pIsolate);
317 return;
318 }
319 CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
320 *pValue = fxv8::NewNumberHelper(
321 pIsolate, occur ? occur->GetMin() : CXFA_Occur::kDefaultMin);
322 }
323
count(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)324 void CJX_InstanceManager::count(v8::Isolate* pIsolate,
325 v8::Local<v8::Value>* pValue,
326 bool bSetting,
327 XFA_Attribute eAttribute) {
328 if (bSetting) {
329 SetInstances(pIsolate, fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
330 return;
331 }
332 *pValue = fxv8::NewNumberHelper(pIsolate, GetXFANode()->GetCount());
333 }
334