• 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/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