• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
11 #include "core/fxcrt/check_op.h"
12 #include "core/fxcrt/span.h"
13 #include "fxjs/fxv8.h"
14 #include "fxjs/js_resources.h"
15 #include "fxjs/xfa/cfxjse_engine.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       const XFA_Element type = pRemoveInstance->GetElementType();
82       if (type != XFA_Element::Subform && type != XFA_Element::SubformSet) {
83         continue;
84       }
85       CHECK_NE(type, XFA_Element::InstanceManager);
86       if (pRemoveInstance->GetNameHash() == dInstanceNameHash) {
87         GetXFANode()->RemoveItem(pRemoveInstance, true);
88         iCount--;
89       }
90     }
91   } else {
92     while (iCount < iDesired) {
93       CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(true);
94       if (!pNewInstance)
95         return 0;
96 
97       GetXFANode()->InsertItem(pNewInstance, iCount, iCount, false);
98       ++iCount;
99 
100       CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
101       if (!pNotify)
102         return 0;
103 
104       pNotify->RunNodeInitialize(pNewInstance);
105     }
106   }
107   GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
108   return 0;
109 }
110 
MoveInstance(v8::Isolate * pIsolate,int32_t iTo,int32_t iFrom)111 int32_t CJX_InstanceManager::MoveInstance(v8::Isolate* pIsolate,
112                                           int32_t iTo,
113                                           int32_t iFrom) {
114   int32_t iCount = GetXFANode()->GetCount();
115   if (iFrom > iCount || iTo > iCount - 1) {
116     ThrowIndexOutOfBoundsException(pIsolate);
117     return 1;
118   }
119   if (iFrom < 0 || iTo < 0 || iFrom == iTo)
120     return 0;
121 
122   CXFA_Node* pMoveInstance = GetXFANode()->GetItemIfExists(iFrom);
123   if (!pMoveInstance) {
124     ThrowIndexOutOfBoundsException(pIsolate);
125     return 1;
126   }
127 
128   GetXFANode()->RemoveItem(pMoveInstance, false);
129   GetXFANode()->InsertItem(pMoveInstance, iTo, iCount - 1, true);
130   GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
131   return 0;
132 }
133 
moveInstance(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)134 CJS_Result CJX_InstanceManager::moveInstance(
135     CFXJSE_Engine* runtime,
136     pdfium::span<v8::Local<v8::Value>> params) {
137   CXFA_Document* doc = runtime->GetDocument();
138   if (doc->GetFormType() != FormType::kXFAFull)
139     return CJS_Result::Failure(JSMessage::kNotSupportedError);
140 
141   if (params.size() != 2)
142     return CJS_Result::Failure(JSMessage::kParamError);
143 
144   int32_t iFrom = runtime->ToInt32(params[0]);
145   int32_t iTo = runtime->ToInt32(params[1]);
146   MoveInstance(runtime->GetIsolate(), iTo, iFrom);
147 
148   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
149   if (!pNotify)
150     return CJS_Result::Success();
151 
152   CXFA_Node* pXFA = GetXFANode();
153   auto* pToInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iTo));
154   if (pToInstance)
155     pNotify->RunSubformIndexChange(pToInstance);
156 
157   auto* pFromInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iFrom));
158   if (pFromInstance)
159     pNotify->RunSubformIndexChange(pFromInstance);
160 
161   return CJS_Result::Success();
162 }
163 
removeInstance(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)164 CJS_Result CJX_InstanceManager::removeInstance(
165     CFXJSE_Engine* runtime,
166     pdfium::span<v8::Local<v8::Value>> params) {
167   CXFA_Document* doc = runtime->GetDocument();
168   if (doc->GetFormType() != FormType::kXFAFull)
169     return CJS_Result::Failure(JSMessage::kNotSupportedError);
170 
171   if (params.size() != 1)
172     return CJS_Result::Failure(JSMessage::kParamError);
173 
174   int32_t iIndex = runtime->ToInt32(params[0]);
175   int32_t iCount = GetXFANode()->GetCount();
176   if (iIndex < 0 || iIndex >= iCount)
177     return CJS_Result::Failure(JSMessage::kInvalidInputError);
178 
179   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
180   int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
181   if (iCount - 1 < iMin)
182     return CJS_Result::Failure(JSMessage::kTooManyOccurrences);
183 
184   CXFA_Node* pRemoveInstance = GetXFANode()->GetItemIfExists(iIndex);
185   if (!pRemoveInstance)
186     return CJS_Result::Failure(JSMessage::kParamError);
187 
188   GetXFANode()->RemoveItem(pRemoveInstance, true);
189 
190   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
191   if (pNotify) {
192     CXFA_Node* pXFA = GetXFANode();
193     for (int32_t i = iIndex; i < iCount - 1; i++) {
194       auto* pSubformInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(i));
195       if (pSubformInstance)
196         pNotify->RunSubformIndexChange(pSubformInstance);
197     }
198   }
199   GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
200   return CJS_Result::Success();
201 }
202 
setInstances(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)203 CJS_Result CJX_InstanceManager::setInstances(
204     CFXJSE_Engine* runtime,
205     pdfium::span<v8::Local<v8::Value>> params) {
206   CXFA_Document* doc = runtime->GetDocument();
207   if (doc->GetFormType() != FormType::kXFAFull)
208     return CJS_Result::Failure(JSMessage::kNotSupportedError);
209 
210   if (params.size() != 1)
211     return CJS_Result::Failure(JSMessage::kParamError);
212 
213   SetInstances(runtime->GetIsolate(), runtime->ToInt32(params[0]));
214   return CJS_Result::Success();
215 }
216 
addInstance(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)217 CJS_Result CJX_InstanceManager::addInstance(
218     CFXJSE_Engine* runtime,
219     pdfium::span<v8::Local<v8::Value>> params) {
220   CXFA_Document* doc = runtime->GetDocument();
221   if (doc->GetFormType() != FormType::kXFAFull)
222     return CJS_Result::Failure(JSMessage::kNotSupportedError);
223 
224   if (!params.empty() && params.size() != 1)
225     return CJS_Result::Failure(JSMessage::kParamError);
226 
227   bool fFlags = true;
228   if (params.size() == 1)
229     fFlags = runtime->ToBoolean(params[0]);
230 
231   int32_t iCount = GetXFANode()->GetCount();
232   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
233   int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
234   if (iMax >= 0 && iCount >= iMax)
235     return CJS_Result::Failure(JSMessage::kTooManyOccurrences);
236 
237   CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(fFlags);
238   if (!pNewInstance)
239     return CJS_Result::Success(runtime->NewNull());
240 
241   GetXFANode()->InsertItem(pNewInstance, iCount, iCount, false);
242 
243   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
244   if (pNotify) {
245     pNotify->RunNodeInitialize(pNewInstance);
246     GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
247   }
248 
249   return CJS_Result::Success(
250       runtime->GetOrCreateJSBindingFromMap(pNewInstance));
251 }
252 
insertInstance(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)253 CJS_Result CJX_InstanceManager::insertInstance(
254     CFXJSE_Engine* runtime,
255     pdfium::span<v8::Local<v8::Value>> params) {
256   CXFA_Document* doc = runtime->GetDocument();
257   if (doc->GetFormType() != FormType::kXFAFull)
258     return CJS_Result::Failure(JSMessage::kNotSupportedError);
259 
260   if (params.size() != 1 && params.size() != 2)
261     return CJS_Result::Failure(JSMessage::kParamError);
262 
263   int32_t iIndex = runtime->ToInt32(params[0]);
264   bool bBind = false;
265   if (params.size() == 2)
266     bBind = runtime->ToBoolean(params[1]);
267 
268   int32_t iCount = GetXFANode()->GetCount();
269   if (iIndex < 0 || iIndex > iCount)
270     return CJS_Result::Failure(JSMessage::kInvalidInputError);
271 
272   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
273   int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax;
274   if (iMax >= 0 && iCount >= iMax)
275     return CJS_Result::Failure(JSMessage::kInvalidInputError);
276 
277   CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(bBind);
278   if (!pNewInstance)
279     return CJS_Result::Success(runtime->NewNull());
280 
281   GetXFANode()->InsertItem(pNewInstance, iIndex, iCount, true);
282 
283   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
284   if (pNotify) {
285     pNotify->RunNodeInitialize(pNewInstance);
286     GetDocument()->GetLayoutProcessor()->SetHasChangedContainer();
287   }
288 
289   return CJS_Result::Success(
290       runtime->GetOrCreateJSBindingFromMap(pNewInstance));
291 }
292 
max(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)293 void CJX_InstanceManager::max(v8::Isolate* pIsolate,
294                               v8::Local<v8::Value>* pValue,
295                               bool bSetting,
296                               XFA_Attribute eAttribute) {
297   if (bSetting) {
298     ThrowInvalidPropertyException(pIsolate);
299     return;
300   }
301   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
302   *pValue = fxv8::NewNumberHelper(
303       pIsolate, occur ? occur->GetMax() : CXFA_Occur::kDefaultMax);
304 }
305 
min(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)306 void CJX_InstanceManager::min(v8::Isolate* pIsolate,
307                               v8::Local<v8::Value>* pValue,
308                               bool bSetting,
309                               XFA_Attribute eAttribute) {
310   if (bSetting) {
311     ThrowInvalidPropertyException(pIsolate);
312     return;
313   }
314   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
315   *pValue = fxv8::NewNumberHelper(
316       pIsolate, occur ? occur->GetMin() : CXFA_Occur::kDefaultMin);
317 }
318 
count(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)319 void CJX_InstanceManager::count(v8::Isolate* pIsolate,
320                                 v8::Local<v8::Value>* pValue,
321                                 bool bSetting,
322                                 XFA_Attribute eAttribute) {
323   if (bSetting) {
324     SetInstances(pIsolate, fxv8::ReentrantToInt32Helper(pIsolate, *pValue));
325     return;
326   }
327   *pValue = fxv8::NewNumberHelper(pIsolate, GetXFANode()->GetCount());
328 }
329