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