• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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