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