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_field.h"
8
9 #include <vector>
10
11 #include "fxjs/cfx_v8.h"
12 #include "fxjs/fxv8.h"
13 #include "fxjs/js_resources.h"
14 #include "third_party/base/numerics/safe_conversions.h"
15 #include "v8/include/v8-primitive.h"
16 #include "xfa/fgas/crt/cfgas_decimal.h"
17 #include "xfa/fxfa/cxfa_eventparam.h"
18 #include "xfa/fxfa/cxfa_ffnotify.h"
19 #include "xfa/fxfa/fxfa.h"
20 #include "xfa/fxfa/parser/cxfa_document.h"
21 #include "xfa/fxfa/parser/cxfa_field.h"
22 #include "xfa/fxfa/parser/cxfa_value.h"
23
24 const CJX_MethodSpec CJX_Field::MethodSpecs[] = {
25 {"addItem", addItem_static},
26 {"boundItem", boundItem_static},
27 {"clearItems", clearItems_static},
28 {"deleteItem", deleteItem_static},
29 {"execCalculate", execCalculate_static},
30 {"execEvent", execEvent_static},
31 {"execInitialize", execInitialize_static},
32 {"execValidate", execValidate_static},
33 {"getDisplayItem", getDisplayItem_static},
34 {"getItemState", getItemState_static},
35 {"getSaveItem", getSaveItem_static},
36 {"setItemState", setItemState_static}};
37
CJX_Field(CXFA_Field * field)38 CJX_Field::CJX_Field(CXFA_Field* field) : CJX_Container(field) {
39 DefineMethods(MethodSpecs);
40 }
41
42 CJX_Field::~CJX_Field() = default;
43
DynamicTypeIs(TypeTag eType) const44 bool CJX_Field::DynamicTypeIs(TypeTag eType) const {
45 return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
46 }
47
clearItems(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)48 CJS_Result CJX_Field::clearItems(
49 CFXJSE_Engine* runtime,
50 const std::vector<v8::Local<v8::Value>>& params) {
51 CXFA_Node* node = GetXFANode();
52 if (node->IsWidgetReady())
53 node->DeleteItem(-1, true, false);
54 return CJS_Result::Success();
55 }
56
execEvent(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)57 CJS_Result CJX_Field::execEvent(
58 CFXJSE_Engine* runtime,
59 const std::vector<v8::Local<v8::Value>>& params) {
60 if (params.size() != 1)
61 return CJS_Result::Failure(JSMessage::kParamError);
62
63 WideString eventString = runtime->ToWideString(params[0]);
64 XFA_EventError iRet =
65 execSingleEventByName(eventString.AsStringView(), XFA_Element::Field);
66 if (!eventString.EqualsASCII("validate"))
67 return CJS_Result::Success();
68
69 return CJS_Result::Success(
70 runtime->NewBoolean(iRet != XFA_EventError::kError));
71 }
72
execInitialize(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)73 CJS_Result CJX_Field::execInitialize(
74 CFXJSE_Engine* runtime,
75 const std::vector<v8::Local<v8::Value>>& params) {
76 if (!params.empty())
77 return CJS_Result::Failure(JSMessage::kParamError);
78
79 CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
80 if (pNotify) {
81 pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Initialize, false,
82 false);
83 }
84 return CJS_Result::Success();
85 }
86
deleteItem(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)87 CJS_Result CJX_Field::deleteItem(
88 CFXJSE_Engine* runtime,
89 const std::vector<v8::Local<v8::Value>>& params) {
90 if (params.size() != 1)
91 return CJS_Result::Failure(JSMessage::kParamError);
92
93 CXFA_Node* node = GetXFANode();
94 if (!node->IsWidgetReady())
95 return CJS_Result::Success();
96
97 bool bValue = node->DeleteItem(runtime->ToInt32(params[0]), true, true);
98 return CJS_Result::Success(runtime->NewBoolean(bValue));
99 }
100
getSaveItem(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)101 CJS_Result CJX_Field::getSaveItem(
102 CFXJSE_Engine* runtime,
103 const std::vector<v8::Local<v8::Value>>& params) {
104 if (params.size() != 1)
105 return CJS_Result::Failure(JSMessage::kParamError);
106
107 int32_t iIndex = runtime->ToInt32(params[0]);
108 if (iIndex < 0)
109 return CJS_Result::Success(runtime->NewNull());
110
111 CXFA_Node* node = GetXFANode();
112 if (!node->IsWidgetReady())
113 return CJS_Result::Success(runtime->NewNull());
114
115 absl::optional<WideString> value = node->GetChoiceListItem(iIndex, true);
116 if (!value.has_value())
117 return CJS_Result::Success(runtime->NewNull());
118
119 return CJS_Result::Success(
120 runtime->NewString(value->ToUTF8().AsStringView()));
121 }
122
boundItem(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)123 CJS_Result CJX_Field::boundItem(
124 CFXJSE_Engine* runtime,
125 const std::vector<v8::Local<v8::Value>>& params) {
126 if (params.size() != 1)
127 return CJS_Result::Failure(JSMessage::kParamError);
128
129 CXFA_Node* node = GetXFANode();
130 if (!node->IsWidgetReady())
131 return CJS_Result::Success();
132
133 WideString value = runtime->ToWideString(params[0]);
134 WideString boundValue = node->GetItemValue(value.AsStringView());
135 return CJS_Result::Success(
136 runtime->NewString(boundValue.ToUTF8().AsStringView()));
137 }
138
getItemState(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)139 CJS_Result CJX_Field::getItemState(
140 CFXJSE_Engine* runtime,
141 const std::vector<v8::Local<v8::Value>>& params) {
142 if (params.size() != 1)
143 return CJS_Result::Failure(JSMessage::kParamError);
144
145 CXFA_Node* node = GetXFANode();
146 if (!node->IsWidgetReady())
147 return CJS_Result::Success();
148
149 int32_t state = node->GetItemState(runtime->ToInt32(params[0]));
150 return CJS_Result::Success(runtime->NewBoolean(state != 0));
151 }
152
execCalculate(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)153 CJS_Result CJX_Field::execCalculate(
154 CFXJSE_Engine* runtime,
155 const std::vector<v8::Local<v8::Value>>& params) {
156 if (!params.empty())
157 return CJS_Result::Failure(JSMessage::kParamError);
158
159 CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
160 if (pNotify) {
161 pNotify->ExecEventByDeepFirst(GetXFANode(), XFA_EVENT_Calculate, false,
162 false);
163 }
164 return CJS_Result::Success();
165 }
166
getDisplayItem(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)167 CJS_Result CJX_Field::getDisplayItem(
168 CFXJSE_Engine* runtime,
169 const std::vector<v8::Local<v8::Value>>& params) {
170 if (params.size() != 1)
171 return CJS_Result::Failure(JSMessage::kParamError);
172
173 int32_t iIndex = runtime->ToInt32(params[0]);
174 if (iIndex < 0)
175 return CJS_Result::Success(runtime->NewNull());
176
177 CXFA_Node* node = GetXFANode();
178 if (!node->IsWidgetReady())
179 return CJS_Result::Success(runtime->NewNull());
180
181 absl::optional<WideString> value = node->GetChoiceListItem(iIndex, false);
182 if (!value.has_value())
183 return CJS_Result::Success(runtime->NewNull());
184
185 return CJS_Result::Success(
186 runtime->NewString(value->ToUTF8().AsStringView()));
187 }
188
setItemState(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)189 CJS_Result CJX_Field::setItemState(
190 CFXJSE_Engine* runtime,
191 const std::vector<v8::Local<v8::Value>>& params) {
192 if (params.size() != 2)
193 return CJS_Result::Failure(JSMessage::kParamError);
194
195 CXFA_Node* node = GetXFANode();
196 if (!node->IsWidgetReady())
197 return CJS_Result::Success();
198
199 int32_t iIndex = runtime->ToInt32(params[0]);
200 if (runtime->ToInt32(params[1]) != 0) {
201 node->SetItemState(iIndex, true, true, true);
202 return CJS_Result::Success();
203 }
204 if (node->GetItemState(iIndex))
205 node->SetItemState(iIndex, false, true, true);
206
207 return CJS_Result::Success();
208 }
209
addItem(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)210 CJS_Result CJX_Field::addItem(CFXJSE_Engine* runtime,
211 const std::vector<v8::Local<v8::Value>>& params) {
212 if (params.size() != 1 && params.size() != 2)
213 return CJS_Result::Failure(JSMessage::kParamError);
214
215 CXFA_Node* node = GetXFANode();
216 if (!node->IsWidgetReady())
217 return CJS_Result::Success();
218
219 WideString label;
220 if (params.size() >= 1)
221 label = runtime->ToWideString(params[0]);
222
223 WideString value;
224 if (params.size() >= 2)
225 value = runtime->ToWideString(params[1]);
226
227 node->InsertItem(label, value, true);
228 return CJS_Result::Success();
229 }
230
execValidate(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)231 CJS_Result CJX_Field::execValidate(
232 CFXJSE_Engine* runtime,
233 const std::vector<v8::Local<v8::Value>>& params) {
234 if (!params.empty())
235 return CJS_Result::Failure(JSMessage::kParamError);
236
237 CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
238 if (!pNotify)
239 return CJS_Result::Success(runtime->NewBoolean(false));
240
241 XFA_EventError iRet = pNotify->ExecEventByDeepFirst(
242 GetXFANode(), XFA_EVENT_Validate, false, false);
243 return CJS_Result::Success(
244 runtime->NewBoolean(iRet != XFA_EventError::kError));
245 }
246
defaultValue(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)247 void CJX_Field::defaultValue(v8::Isolate* pIsolate,
248 v8::Local<v8::Value>* pValue,
249 bool bSetting,
250 XFA_Attribute eAttribute) {
251 CXFA_Node* xfaNode = GetXFANode();
252 if (!xfaNode->IsWidgetReady())
253 return;
254
255 if (bSetting) {
256 if (pValue) {
257 xfaNode->SetPreNull(xfaNode->IsNull());
258 xfaNode->SetIsNull(fxv8::IsNull(*pValue));
259 }
260
261 WideString wsNewText;
262 if (pValue && !(fxv8::IsNull(*pValue) || fxv8::IsUndefined(*pValue)))
263 wsNewText = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue);
264 if (xfaNode->GetUIChildNode()->GetElementType() == XFA_Element::NumericEdit)
265 wsNewText = xfaNode->NumericLimit(wsNewText);
266
267 CXFA_Node* pContainerNode = xfaNode->GetContainerNode();
268 WideString wsFormatText(wsNewText);
269 if (pContainerNode)
270 wsFormatText = pContainerNode->GetFormatDataValue(wsNewText);
271
272 SetContent(wsNewText, wsFormatText, true, true, true);
273 return;
274 }
275
276 WideString content = GetContent(true);
277 if (content.IsEmpty()) {
278 *pValue = fxv8::NewNullHelper(pIsolate);
279 return;
280 }
281
282 CXFA_Node* formValue = xfaNode->GetFormValueIfExists();
283 CXFA_Node* pNode = formValue ? formValue->GetFirstChild() : nullptr;
284 if (pNode && pNode->GetElementType() == XFA_Element::Decimal) {
285 if (xfaNode->GetUIChildNode()->GetElementType() ==
286 XFA_Element::NumericEdit &&
287 (pNode->JSObject()->GetInteger(XFA_Attribute::FracDigits) == -1)) {
288 *pValue =
289 fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
290 } else {
291 CFGAS_Decimal decimal(content.AsStringView());
292 *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat());
293 }
294 } else if (pNode && pNode->GetElementType() == XFA_Element::Integer) {
295 *pValue = fxv8::NewNumberHelper(pIsolate, FXSYS_wtoi(content.c_str()));
296 } else if (pNode && pNode->GetElementType() == XFA_Element::Boolean) {
297 *pValue =
298 fxv8::NewBooleanHelper(pIsolate, FXSYS_wtoi(content.c_str()) != 0);
299 } else if (pNode && pNode->GetElementType() == XFA_Element::Float) {
300 CFGAS_Decimal decimal(content.AsStringView());
301 *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat());
302 } else {
303 *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView());
304 }
305 }
306
editValue(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)307 void CJX_Field::editValue(v8::Isolate* pIsolate,
308 v8::Local<v8::Value>* pValue,
309 bool bSetting,
310 XFA_Attribute eAttribute) {
311 CXFA_Node* node = GetXFANode();
312 if (!node->IsWidgetReady())
313 return;
314
315 if (bSetting) {
316 node->SetValue(XFA_ValuePicture::kEdit,
317 fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
318 return;
319 }
320 *pValue = fxv8::NewStringHelper(
321 pIsolate,
322 node->GetValue(XFA_ValuePicture::kEdit).ToUTF8().AsStringView());
323 }
324
formatMessage(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)325 void CJX_Field::formatMessage(v8::Isolate* pIsolate,
326 v8::Local<v8::Value>* pValue,
327 bool bSetting,
328 XFA_Attribute eAttribute) {
329 ScriptSomMessage(pIsolate, pValue, bSetting, SOMMessageType::kFormatMessage);
330 }
331
formattedValue(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)332 void CJX_Field::formattedValue(v8::Isolate* pIsolate,
333 v8::Local<v8::Value>* pValue,
334 bool bSetting,
335 XFA_Attribute eAttribute) {
336 CXFA_Node* node = GetXFANode();
337 if (!node->IsWidgetReady())
338 return;
339
340 if (bSetting) {
341 node->SetValue(XFA_ValuePicture::kDisplay,
342 fxv8::ReentrantToWideStringHelper(pIsolate, *pValue));
343 return;
344 }
345 *pValue = fxv8::NewStringHelper(
346 pIsolate,
347 node->GetValue(XFA_ValuePicture::kDisplay).ToUTF8().AsStringView());
348 }
349
length(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)350 void CJX_Field::length(v8::Isolate* pIsolate,
351 v8::Local<v8::Value>* pValue,
352 bool bSetting,
353 XFA_Attribute eAttribute) {
354 if (bSetting) {
355 ThrowInvalidPropertyException(pIsolate);
356 return;
357 }
358
359 CXFA_Node* node = GetXFANode();
360 *pValue = fxv8::NewNumberHelper(
361 pIsolate, node->IsWidgetReady() ? pdfium::base::checked_cast<int>(
362 node->CountChoiceListItems(true))
363 : 0);
364 }
365
parentSubform(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)366 void CJX_Field::parentSubform(v8::Isolate* pIsolate,
367 v8::Local<v8::Value>* pValue,
368 bool bSetting,
369 XFA_Attribute eAttribute) {
370 if (bSetting) {
371 ThrowInvalidPropertyException(pIsolate);
372 return;
373 }
374 *pValue = fxv8::NewNullHelper(pIsolate);
375 }
376
selectedIndex(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)377 void CJX_Field::selectedIndex(v8::Isolate* pIsolate,
378 v8::Local<v8::Value>* pValue,
379 bool bSetting,
380 XFA_Attribute eAttribute) {
381 CXFA_Node* node = GetXFANode();
382 if (!node->IsWidgetReady())
383 return;
384
385 if (!bSetting) {
386 *pValue = fxv8::NewNumberHelper(pIsolate, node->GetSelectedItem(0));
387 return;
388 }
389
390 int32_t iIndex = fxv8::ReentrantToInt32Helper(pIsolate, *pValue);
391 if (iIndex == -1) {
392 node->ClearAllSelections();
393 return;
394 }
395
396 node->SetItemState(iIndex, true, true, true);
397 }
398
rawValue(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)399 void CJX_Field::rawValue(v8::Isolate* pIsolate,
400 v8::Local<v8::Value>* pValue,
401 bool bSetting,
402 XFA_Attribute eAttribute) {
403 defaultValue(pIsolate, pValue, bSetting, eAttribute);
404 }
405