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