1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.xml;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Iterator;
20 import java.util.List;
21
22 import org.apache.commons.lang3.StringUtils;
23 import org.htmlunit.FormEncodingType;
24 import org.htmlunit.WebRequest;
25 import org.htmlunit.corejs.javascript.Context;
26 import org.htmlunit.corejs.javascript.ES6Iterator;
27 import org.htmlunit.corejs.javascript.Function;
28 import org.htmlunit.corejs.javascript.Scriptable;
29 import org.htmlunit.corejs.javascript.ScriptableObject;
30 import org.htmlunit.javascript.HtmlUnitScriptable;
31 import org.htmlunit.javascript.JavaScriptEngine;
32 import org.htmlunit.javascript.configuration.JsxClass;
33 import org.htmlunit.javascript.configuration.JsxConstructor;
34 import org.htmlunit.javascript.configuration.JsxFunction;
35 import org.htmlunit.javascript.configuration.JsxSymbol;
36 import org.htmlunit.javascript.host.file.Blob;
37 import org.htmlunit.javascript.host.file.File;
38 import org.htmlunit.javascript.host.html.HTMLFormElement;
39 import org.htmlunit.util.NameValuePair;
40
41
42
43
44
45
46
47
48 @JsxClass
49 public class FormData extends HtmlUnitScriptable {
50
51
52 public static final String FORM_DATA_TAG = "FormData";
53
54 private final List<NameValuePair> requestParameters_ = new ArrayList<>();
55
56
57
58
59 public static final class FormDataIterator extends ES6Iterator {
60 enum Type { KEYS, VALUES, BOTH }
61
62 private final Type type_;
63 private final String className_;
64 private final List<NameValuePair> nameValuePairList_;
65 private int index_;
66
67
68
69
70
71
72
73 public static void init(final ScriptableObject scope, final String className) {
74 ES6Iterator.init(scope, false, new FormDataIterator(className), FORM_DATA_TAG);
75 }
76
77
78
79
80
81
82 public FormDataIterator(final String className) {
83 super();
84
85 type_ = Type.BOTH;
86 index_ = 0;
87 nameValuePairList_ = Collections.emptyList();
88 className_ = className;
89 }
90
91
92
93
94
95
96
97
98
99 public FormDataIterator(final Scriptable scope, final String className, final Type type,
100 final List<NameValuePair> nameValuePairList) {
101 super(scope, FORM_DATA_TAG);
102 type_ = type;
103 index_ = 0;
104 nameValuePairList_ = nameValuePairList;
105 className_ = className;
106 }
107
108
109
110
111 @Override
112 public String getClassName() {
113 return className_;
114 }
115
116
117
118
119 @Override
120 protected boolean isDone(final Context cx, final Scriptable scope) {
121 return index_ >= nameValuePairList_.size();
122 }
123
124
125
126
127 @Override
128 protected Object nextValue(final Context cx, final Scriptable scope) {
129 if (isDone(cx, scope)) {
130 return Context.getUndefinedValue();
131 }
132
133 final NameValuePair nextNameValuePair = nameValuePairList_.get(index_++);
134 switch (type_) {
135 case KEYS:
136 return nextNameValuePair.getName();
137 case VALUES:
138 return nextNameValuePair.getValue();
139 case BOTH:
140 return cx.newArray(scope, new Object[] {nextNameValuePair.getName(), nextNameValuePair.getValue()});
141 default:
142 throw new AssertionError();
143 }
144 }
145 }
146
147
148
149
150
151 @JsxConstructor
152 public void jsConstructor(final Object formObj) {
153 if (formObj instanceof HTMLFormElement) {
154 final HTMLFormElement form = (HTMLFormElement) formObj;
155 requestParameters_.addAll(form.getHtmlForm().getParameterListForSubmit(null));
156 }
157 }
158
159
160
161
162
163
164
165
166 @JsxFunction
167 public void append(final String name, final Object value, final Object filename) {
168 if (value instanceof Blob) {
169 final Blob blob = (Blob) value;
170 String fileName = "blob";
171 if (value instanceof File) {
172 fileName = null;
173 }
174 if (filename instanceof String) {
175 fileName = (String) filename;
176 }
177 requestParameters_.add(blob.getKeyDataPair(name, fileName));
178 return;
179 }
180 requestParameters_.add(new NameValuePair(name, JavaScriptEngine.toString(value)));
181 }
182
183
184
185
186
187 @JsxFunction(functionName = "delete")
188 public void delete_js(final String name) {
189 if (StringUtils.isEmpty(name)) {
190 return;
191 }
192
193 requestParameters_.removeIf(pair -> name.equals(pair.getName()));
194 }
195
196
197
198
199
200 @JsxFunction
201 public String get(final String name) {
202 if (StringUtils.isEmpty(name)) {
203 return null;
204 }
205
206 for (final NameValuePair pair : requestParameters_) {
207 if (name.equals(pair.getName())) {
208 return pair.getValue();
209 }
210 }
211 return null;
212 }
213
214
215
216
217
218 @JsxFunction
219 public Scriptable getAll(final String name) {
220 if (StringUtils.isEmpty(name)) {
221 return JavaScriptEngine.newArray(this, 0);
222 }
223
224 final List<Object> values = new ArrayList<>();
225 for (final NameValuePair pair : requestParameters_) {
226 if (name.equals(pair.getName())) {
227 values.add(pair.getValue());
228 }
229 }
230
231 final Object[] stringValues = values.toArray(new Object[0]);
232 return JavaScriptEngine.newArray(this, stringValues);
233 }
234
235
236
237
238
239 @JsxFunction
240 public boolean has(final String name) {
241 if (StringUtils.isEmpty(name)) {
242 return false;
243 }
244
245 for (final NameValuePair pair : requestParameters_) {
246 if (name.equals(pair.getName())) {
247 return true;
248 }
249 }
250 return false;
251 }
252
253
254
255
256
257
258
259
260 @JsxFunction
261 public void set(final String name, final Object value, final Object filename) {
262 if (StringUtils.isEmpty(name)) {
263 return;
264 }
265
266 int pos = -1;
267
268 final Iterator<NameValuePair> iter = requestParameters_.iterator();
269 int idx = 0;
270 while (iter.hasNext()) {
271 final NameValuePair pair = iter.next();
272 if (name.equals(pair.getName())) {
273 iter.remove();
274 if (pos < 0) {
275 pos = idx;
276 }
277 }
278 idx++;
279 }
280
281 if (pos < 0) {
282 pos = requestParameters_.size();
283 }
284
285 if (value instanceof Blob) {
286 final Blob blob = (Blob) value;
287 String fileName = "blob";
288 if (value instanceof File) {
289 fileName = null;
290 }
291 if (filename instanceof String) {
292 fileName = (String) filename;
293 }
294 requestParameters_.add(pos, blob.getKeyDataPair(name, fileName));
295 }
296 else {
297 requestParameters_.add(pos, new NameValuePair(name, JavaScriptEngine.toString(value)));
298 }
299 }
300
301
302
303
304 @JsxFunction
305 @JsxSymbol(symbolName = "iterator")
306 public Scriptable entries() {
307 return new FormDataIterator(this, "FormData Iterator", FormDataIterator.Type.BOTH, requestParameters_);
308 }
309
310
311
312
313
314 public void fillRequest(final WebRequest webRequest) {
315 webRequest.setEncodingType(FormEncodingType.MULTIPART);
316 webRequest.setRequestParameters(requestParameters_);
317 }
318
319
320
321
322
323
324 @JsxFunction
325 public void forEach(final Object callback) {
326 if (!(callback instanceof Function)) {
327 throw JavaScriptEngine.typeError(
328 "Foreach callback '" + JavaScriptEngine.toString(callback) + "' is not a function");
329 }
330
331 final Function fun = (Function) callback;
332
333
334 for (int i = 0;; i++) {
335 if (i >= requestParameters_.size()) {
336 break;
337 }
338
339 final NameValuePair param = requestParameters_.get(i);
340 fun.call(Context.getCurrentContext(), getParentScope(), this,
341 new Object[] {param.getValue(), param.getName(), this});
342 }
343 }
344
345
346
347
348
349
350
351 @JsxFunction
352 public FormDataIterator keys() {
353 return new FormDataIterator(getParentScope(),
354 "FormData Iterator", FormDataIterator.Type.KEYS, requestParameters_);
355 }
356
357
358
359
360
361
362
363 @JsxFunction
364 public FormDataIterator values() {
365 return new FormDataIterator(getParentScope(),
366 "FormData Iterator", FormDataIterator.Type.VALUES, requestParameters_);
367 }
368 }