View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.html;
16  
17  import static java.nio.charset.StandardCharsets.UTF_8;
18  
19  import java.io.BufferedReader;
20  import java.io.ByteArrayOutputStream;
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.Writer;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.net.URI;
27  import java.net.URL;
28  import java.net.URLDecoder;
29  import java.text.Normalizer;
30  import java.text.Normalizer.Form;
31  import java.util.HashMap;
32  import java.util.Locale;
33  import java.util.Map;
34  
35  import javax.servlet.Servlet;
36  import javax.servlet.ServletException;
37  import javax.servlet.http.HttpServlet;
38  import javax.servlet.http.HttpServletRequest;
39  import javax.servlet.http.HttpServletResponse;
40  
41  import org.apache.commons.fileupload.FileItem;
42  import org.apache.commons.fileupload.FileUploadBase;
43  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
44  import org.apache.commons.fileupload.servlet.ServletFileUpload;
45  import org.apache.http.HttpEntity;
46  import org.apache.http.client.methods.HttpPost;
47  import org.apache.http.impl.client.HttpClientBuilder;
48  import org.htmlunit.HttpWebConnection;
49  import org.htmlunit.MockWebConnection;
50  import org.htmlunit.WebClient;
51  import org.htmlunit.WebRequest;
52  import org.htmlunit.WebServerTestCase;
53  import org.htmlunit.junit.annotation.Alerts;
54  import org.htmlunit.util.KeyDataPair;
55  import org.htmlunit.util.MimeType;
56  import org.junit.jupiter.api.Test;
57  
58  /**
59   * Tests for {@link HtmlFileInput}.
60   *
61   * @author Marc Guillemot
62   * @author Ahmed Ashour
63   * @author Ronald Brill
64   * @author Frank Danek
65   */
66  public class HtmlFileInput2Test extends WebServerTestCase {
67  
68      /**
69       * @throws Exception if the test fails
70       */
71      @Test
72      public void fileInput() throws Exception {
73          final URL fileURL = getClass().getClassLoader().getResource("testfiles/tiny-png.img");
74          final File file = new File(fileURL.toURI());
75          assertTrue("File '" + file.getAbsolutePath() + "' does not exist", file.exists());
76  
77          testFileInput(file);
78  
79          String path = fileURL.getPath();
80          if (path.startsWith("file:")) {
81              path = path.substring("file:".length());
82          }
83          while (path.startsWith("/")) {
84              path = path.substring(1);
85          }
86          if (System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("windows")) {
87              testFileInput(new File(URLDecoder.decode(path.replace('/', '\\'), UTF_8.name())));
88          }
89          testFileInput(new File("file:/" + path));
90          testFileInput(new File("file://" + path));
91          testFileInput(new File("file:///" + path));
92      }
93  
94      private void testFileInput(final File file) throws Exception {
95          final String firstContent = DOCTYPE_HTML
96              + "<html><head></head><body>\n"
97              + "<form enctype='multipart/form-data' action='" + URL_SECOND + "' method='POST'>\n"
98              + "  <input type='file' name='image'>\n"
99              + "  <input type='submit' id='clickMe'>\n"
100             + "</form>\n"
101             + "</body>\n"
102             + "</html>";
103         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head></html>";
104         final WebClient client = getWebClient();
105 
106         final MockWebConnection webConnection = new MockWebConnection();
107         webConnection.setResponse(URL_FIRST, firstContent);
108         webConnection.setResponse(URL_SECOND, secondContent);
109 
110         client.setWebConnection(webConnection);
111 
112         final HtmlPage firstPage = client.getPage(URL_FIRST);
113         final HtmlForm f = firstPage.getForms().get(0);
114         final HtmlFileInput fileInput = f.getInputByName("image");
115         fileInput.setFiles(file);
116         firstPage.getHtmlElementById("clickMe").click();
117         final KeyDataPair pair = (KeyDataPair) webConnection.getLastParameters().get(0);
118         assertNotNull(pair.getFile());
119         assertTrue(pair.getFile().length() != 0);
120     }
121 
122     /**
123      * Tests setValueAttribute method.
124      * @throws Exception if the test fails
125      */
126     @Test
127     public void setValueAttributeAndSetDataDummyFile() throws Exception {
128         final String firstContent = DOCTYPE_HTML
129             + "<html><head></head><body>\n"
130             + "<form enctype='multipart/form-data' action='" + URL_SECOND + "' method='POST'>\n"
131             + "  <input type='file' name='image'>\n"
132             + "  <input type='submit' id='mySubmit'>\n"
133             + "</form>\n"
134             + "</body>\n"
135             + "</html>";
136         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head></html>";
137         final WebClient client = getWebClient();
138 
139         final MockWebConnection webConnection = new MockWebConnection();
140         webConnection.setResponse(URL_FIRST, firstContent);
141         webConnection.setResponse(URL_SECOND, secondContent);
142 
143         client.setWebConnection(webConnection);
144 
145         final HtmlPage firstPage = client.getPage(URL_FIRST);
146         final HtmlForm f = firstPage.getForms().get(0);
147         final HtmlFileInput fileInput = f.getInputByName("image");
148         fileInput.setValueAttribute("dummy.txt");
149         fileInput.setContentType("text/csv");
150         fileInput.setData("My file data".getBytes());
151         firstPage.getHtmlElementById("mySubmit").click();
152         final KeyDataPair pair = (KeyDataPair) webConnection.getLastParameters().get(0);
153 
154         assertNull(pair.getData());
155 
156         final HttpEntity httpEntity = post(client, webConnection);
157 
158         try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
159             httpEntity.writeTo(out);
160 
161             assertFalse(out.toString().contains("dummy.txt"));
162         }
163     }
164 
165     /**
166      * Tests setData method.
167      * @throws Exception if the test fails
168      */
169     @Test
170     public void setValueAndSetDataDummyFile() throws Exception {
171         final String firstContent = DOCTYPE_HTML
172             + "<html><head></head><body>\n"
173             + "<form enctype='multipart/form-data' action='" + URL_SECOND + "' method='POST'>\n"
174             + "  <input type='file' name='image'>\n"
175             + "  <input type='submit' id='mySubmit'>\n"
176             + "</form>\n"
177             + "</body>\n"
178             + "</html>";
179         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head></html>";
180         final WebClient client = getWebClient();
181 
182         final MockWebConnection webConnection = new MockWebConnection();
183         webConnection.setResponse(URL_FIRST, firstContent);
184         webConnection.setResponse(URL_SECOND, secondContent);
185 
186         client.setWebConnection(webConnection);
187 
188         final HtmlPage firstPage = client.getPage(URL_FIRST);
189         final HtmlForm f = firstPage.getForms().get(0);
190         final HtmlFileInput fileInput = f.getInputByName("image");
191         fileInput.setValue("dummy.txt");
192         fileInput.setContentType("text/csv");
193         fileInput.setData("My file data".getBytes());
194         firstPage.getHtmlElementById("mySubmit").click();
195         final KeyDataPair pair = (KeyDataPair) webConnection.getLastParameters().get(0);
196 
197         assertNotNull(pair.getData());
198         assertTrue(pair.getData().length > 10);
199 
200         final HttpEntity httpEntity = post(client, webConnection);
201 
202         try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
203             httpEntity.writeTo(out);
204 
205             assertTrue(out.toString().contains("dummy.txt"));
206         }
207     }
208 
209     /**
210      * Tests setValueAttribute method.
211      * @throws Exception if the test fails
212      */
213     @Test
214     public void setValueAttributeAndSetDataRealFile() throws Exception {
215         final String firstContent = DOCTYPE_HTML
216             + "<html><head></head><body>\n"
217             + "<form enctype='multipart/form-data' action='" + URL_SECOND + "' method='POST'>\n"
218             + "  <input type='file' name='image'>\n"
219             + "  <input type='submit' id='mySubmit'>\n"
220             + "</form>\n"
221             + "</body>\n"
222             + "</html>";
223         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head></html>";
224         final WebClient client = getWebClient();
225 
226         final MockWebConnection webConnection = new MockWebConnection();
227         webConnection.setResponse(URL_FIRST, firstContent);
228         webConnection.setResponse(URL_SECOND, secondContent);
229 
230         client.setWebConnection(webConnection);
231 
232         final HtmlPage firstPage = client.getPage(URL_FIRST);
233         final HtmlForm f = firstPage.getForms().get(0);
234         final HtmlFileInput fileInput = f.getInputByName("image");
235         final String path = getClass().getClassLoader().getResource("testfiles/" + "tiny-png.img").toExternalForm();
236         fileInput.setValueAttribute(path);
237         fileInput.setData("My file data".getBytes());
238         firstPage.getHtmlElementById("mySubmit").click();
239         final KeyDataPair pair = (KeyDataPair) webConnection.getLastParameters().get(0);
240 
241         assertNull(pair.getData());
242 
243         final HttpEntity httpEntity = post(client, webConnection);
244 
245         try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
246             httpEntity.writeTo(out);
247 
248             assertFalse(out.toString()
249                     .contains("Content-Disposition: form-data; name=\"image\"; filename=\"tiny-png.img\""));
250         }
251     }
252 
253     /**
254      * Tests setData method.
255      * @throws Exception if the test fails
256      */
257     @Test
258     public void setValueAndSetDataRealFile() throws Exception {
259         final String firstContent = DOCTYPE_HTML
260             + "<html><head></head><body>\n"
261             + "<form enctype='multipart/form-data' action='" + URL_SECOND + "' method='POST'>\n"
262             + "  <input type='file' name='image'>\n"
263             + "  <input type='submit' id='mySubmit'>\n"
264             + "</form>\n"
265             + "</body>\n"
266             + "</html>";
267         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head></html>";
268         final WebClient client = getWebClient();
269 
270         final MockWebConnection webConnection = new MockWebConnection();
271         webConnection.setResponse(URL_FIRST, firstContent);
272         webConnection.setResponse(URL_SECOND, secondContent);
273 
274         client.setWebConnection(webConnection);
275 
276         final HtmlPage firstPage = client.getPage(URL_FIRST);
277         final HtmlForm f = firstPage.getForms().get(0);
278         final HtmlFileInput fileInput = f.getInputByName("image");
279         final String path = getClass().getClassLoader().getResource("testfiles/" + "tiny-png.img").toExternalForm();
280         fileInput.setValue(path);
281         fileInput.setData("My file data".getBytes());
282         firstPage.getHtmlElementById("mySubmit").click();
283         final KeyDataPair pair = (KeyDataPair) webConnection.getLastParameters().get(0);
284 
285         assertNotNull(pair.getData());
286         assertTrue(pair.getData().length > 10);
287 
288         final HttpEntity httpEntity = post(client, webConnection);
289 
290         try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
291             httpEntity.writeTo(out);
292 
293             assertTrue(out.toString()
294                     .contains("Content-Disposition: form-data; name=\"image\"; filename=\"tiny-png.img\""));
295         }
296     }
297 
298     /**
299      * Tests setData method.
300      * @throws Exception if the test fails
301      */
302     @Test
303     public void setDataOnly() throws Exception {
304         final String firstContent = DOCTYPE_HTML
305             + "<html><head></head><body>\n"
306             + "<form enctype='multipart/form-data' action='" + URL_SECOND + "' method='POST'>\n"
307             + "  <input type='file' name='image'>\n"
308             + "  <input type='submit' id='mySubmit'>\n"
309             + "</form>\n"
310             + "</body>\n"
311             + "</html>";
312         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head></html>";
313         final WebClient client = getWebClient();
314 
315         final MockWebConnection webConnection = new MockWebConnection();
316         webConnection.setResponse(URL_FIRST, firstContent);
317         webConnection.setResponse(URL_SECOND, secondContent);
318 
319         client.setWebConnection(webConnection);
320 
321         final HtmlPage firstPage = client.getPage(URL_FIRST);
322         final HtmlForm f = firstPage.getForms().get(0);
323         final HtmlFileInput fileInput = f.getInputByName("image");
324         fileInput.setData("My file data".getBytes());
325         firstPage.getHtmlElementById("mySubmit").click();
326         final KeyDataPair pair = (KeyDataPair) webConnection.getLastParameters().get(0);
327 
328         assertNull(pair.getData());
329 
330         final HttpEntity httpEntity = post(client, webConnection);
331 
332         try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
333             httpEntity.writeTo(out);
334 
335             assertTrue(out.toString()
336                     .contains("Content-Disposition: form-data; name=\"image\"; filename=\"\""));
337         }
338     }
339 
340     /**
341      * Helper that does some nasty magic.
342      */
343     private static HttpEntity post(final WebClient client,
344             final MockWebConnection webConnection)
345             throws NoSuchMethodException, IllegalAccessException,
346             InvocationTargetException {
347         final Method makeHttpMethod = HttpWebConnection.class.getDeclaredMethod("makeHttpMethod",
348                 WebRequest.class, HttpClientBuilder.class);
349         makeHttpMethod.setAccessible(true);
350 
351         final HttpWebConnection con = new HttpWebConnection(client);
352 
353         final Method getHttpClientBuilderMethod = HttpWebConnection.class.getDeclaredMethod("getHttpClientBuilder");
354         getHttpClientBuilderMethod.setAccessible(true);
355         final HttpClientBuilder builder = (HttpClientBuilder) getHttpClientBuilderMethod.invoke(con);
356 
357         final HttpPost httpPost = (HttpPost) makeHttpMethod.invoke(con, webConnection.getLastWebRequest(), builder);
358         final HttpEntity httpEntity = httpPost.getEntity();
359         return httpEntity;
360     }
361 
362     /**
363      * Verifies that content is provided for a not filled file input.
364      * @throws Exception if the test fails
365      */
366     @Test
367     public void emptyField() throws Exception {
368         final String firstContent = DOCTYPE_HTML
369             + "<html><head></head><body>\n"
370             + "<form enctype='multipart/form-data' action='" + URL_SECOND + "' method='POST'>\n"
371             + "  <input type='file' name='image' />\n"
372             + "  <input type='submit' id='clickMe'>\n"
373             + "</form>\n"
374             + "</body>\n"
375             + "</html>";
376         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head></html>";
377         final WebClient client = getWebClient();
378 
379         final MockWebConnection webConnection = new MockWebConnection();
380         webConnection.setResponse(URL_FIRST, firstContent);
381         webConnection.setResponse(URL_SECOND, secondContent);
382 
383         client.setWebConnection(webConnection);
384 
385         final HtmlPage firstPage = client.getPage(URL_FIRST);
386         firstPage.getHtmlElementById("clickMe").click();
387         final KeyDataPair pair = (KeyDataPair) webConnection.getLastParameters().get(0);
388         assertEquals("image", pair.getName());
389         assertNull(pair.getFile());
390     }
391 
392     /**
393      * @throws Exception if the test fails
394      */
395     @Test
396     public void contentType() throws Exception {
397         final String firstContent = DOCTYPE_HTML
398             + "<html><head></head><body>\n"
399             + "<form enctype='multipart/form-data' action='" + URL_SECOND + "' method='POST'>\n"
400             + "  <input type='file' name='image' />\n"
401             + "  <input type='submit' name='mysubmit'/>\n"
402             + "</form>\n"
403             + "</body>\n"
404             + "</html>";
405         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head></html>";
406         final WebClient client = getWebClient();
407 
408         final MockWebConnection webConnection = new MockWebConnection();
409         webConnection.setResponse(URL_FIRST, firstContent);
410         webConnection.setResponse(URL_SECOND, secondContent);
411 
412         client.setWebConnection(webConnection);
413 
414         final HtmlPage firstPage = client.getPage(URL_FIRST);
415         final HtmlForm f = firstPage.getForms().get(0);
416         final HtmlFileInput fileInput = f.getInputByName("image");
417 
418         final URL fileURL = getClass().getClassLoader().getResource("testfiles/empty.png");
419         final File file = new File(fileURL.toURI());
420         assertTrue("File '" + file.getAbsolutePath() + "' does not exist", file.exists());
421 
422         fileInput.setFiles(file);
423         f.getInputByName("mysubmit").click();
424 
425         assertEquals(2, webConnection.getLastParameters().size());
426         KeyDataPair pair = (KeyDataPair) webConnection.getLastParameters().get(0);
427         if ("mysubmit".equals(pair.getName())) {
428             pair = (KeyDataPair) webConnection.getLastParameters().get(1);
429         }
430         assertNotNull(pair.getFile());
431         assertFalse("Content type: " + pair.getMimeType(), "text/webtest".equals(pair.getMimeType()));
432 
433         fileInput.setContentType("text/webtest");
434         f.getInputByName("mysubmit").click();
435 
436         assertEquals(2, webConnection.getLastParameters().size());
437         KeyDataPair pair2 = (KeyDataPair) webConnection.getLastParameters().get(0);
438         if ("mysubmit".equals(pair2.getName())) {
439             pair2 = (KeyDataPair) webConnection.getLastParameters().get(1);
440         }
441         assertNotNull(pair2.getFile());
442         assertEquals("text/webtest", pair2.getMimeType());
443     }
444 
445     /**
446      * Test uploading a file with non-ASCII name.
447      *
448      * Test for http://sourceforge.net/p/htmlunit/bugs/535/
449      *
450      * @throws Exception if the test fails
451      */
452     @Test
453     public void uploadFileWithNonASCIIName() throws Exception {
454         final Map<String, Class<? extends Servlet>> servlets = new HashMap<>();
455         servlets.put("/upload1", Upload1Servlet.class);
456         servlets.put("/upload2", Upload2Servlet.class);
457         startWebServer("./", null, servlets);
458 
459         final String filename = "\u6A94\u6848\uD30C\uC77C\u30D5\u30A1\u30A4\u30EB\u0645\u0644\u0641.txt";
460         final URL fileURL = getClass().getClassLoader().getResource(filename);
461         final File file = new File(fileURL.toURI());
462         assertTrue("File '" + file.getAbsolutePath() + "' does not exist", file.exists());
463 
464         final WebClient client = getWebClient();
465         final HtmlPage firstPage = client.getPage(URL_FIRST + "upload1");
466 
467         final HtmlForm form = firstPage.getForms().get(0);
468         final HtmlFileInput fileInput = form.getInputByName("myInput");
469         fileInput.setFiles(file);
470 
471         final HtmlSubmitInput submitInput = form.getInputByValue("Upload");
472         final HtmlPage secondPage = submitInput.click();
473 
474         final String response = secondPage.getWebResponse().getContentAsString();
475 
476         //this is the value with UTF-8 encoding
477         final String expectedResponse = "6A94 6848 D30C C77C 30D5 30A1 30A4 30EB 645 644 641 2E 74 78 74 <br>myInput";
478 
479         assertTrue("Invalid Response: " + response, response.endsWith(expectedResponse));
480     }
481 
482     /**
483      * Servlet for '/upload1'.
484      */
485     public static class Upload1Servlet extends HttpServlet {
486 
487         /**
488          * {@inheritDoc}
489          */
490         @Override
491         protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
492             throws ServletException, IOException {
493             response.setCharacterEncoding(UTF_8.name());
494             response.setContentType(MimeType.TEXT_HTML);
495             response.getWriter().write("<html>\n"
496                 + "<body><form action='upload2' method='post' enctype='multipart/form-data'>\n"
497                 + "Name: <input name='myInput' type='file'><br>\n"
498                 + "Name 2 (should stay empty): <input name='myInput2' type='file'><br>\n"
499                 + "<input type='submit' value='Upload' id='mySubmit'>\n"
500                 + "</form></body></html>\n");
501         }
502     }
503 
504     /**
505      * Servlet for '/upload2'.
506      */
507     public static class Upload2Servlet extends HttpServlet {
508 
509         /**
510          * {@inheritDoc}
511          */
512         @Override
513         protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
514             throws ServletException, IOException {
515             request.setCharacterEncoding(UTF_8.name());
516             response.setContentType(MimeType.TEXT_HTML);
517             final Writer writer = response.getWriter();
518             if (ServletFileUpload.isMultipartContent(request)) {
519                 try {
520                     final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
521                     for (final FileItem item : upload.parseRequest(request)) {
522                         if ("myInput".equals(item.getFieldName())) {
523                             final String path = item.getName();
524                             for (final char ch : path.toCharArray()) {
525                                 writer.write(Integer.toHexString(ch).toUpperCase(Locale.ROOT) + " ");
526                             }
527                             writer.write("<br>");
528                             writer.write(item.getFieldName());
529                         }
530                     }
531                 }
532                 catch (final FileUploadBase.SizeLimitExceededException e) {
533                     writer.write("SizeLimitExceeded");
534                 }
535                 catch (final Exception e) {
536                     writer.write("error");
537                 }
538             }
539         }
540     }
541 
542     /**
543      * @throws Exception if the test fails
544      */
545     @Test
546     public void mutiple() throws Exception {
547         final Map<String, Class<? extends Servlet>> servlets = new HashMap<>();
548         servlets.put("/upload1", Multiple1Servlet.class);
549         servlets.put("/upload2", PrintRequestServlet.class);
550         startWebServer("./", null, servlets);
551 
552         final String filename1 = "HtmlFileInputTest_one.txt";
553         final String path1 = getClass().getResource(filename1).toExternalForm();
554         final File file1 = new File(new URI(path1));
555         assertTrue(file1.exists());
556 
557         final String filename2 = "HtmlFileInputTest_two.txt";
558         final String path2 = getClass().getResource(filename2).toExternalForm();
559         final File file2 = new File(new URI(path2));
560         assertTrue(file2.exists());
561 
562         final WebClient client = getWebClient();
563         final HtmlPage firstPage = client.getPage(URL_FIRST + "upload1");
564 
565         final HtmlForm form = firstPage.getForms().get(0);
566         final HtmlFileInput fileInput = form.getInputByName("myInput");
567         fileInput.setFiles(file1, file2);
568 
569         final HtmlSubmitInput submitInput = form.getInputByValue("Upload");
570         final HtmlPage secondPage = submitInput.click();
571 
572         final String response = secondPage.getWebResponse().getContentAsString();
573 
574         assertTrue(response.contains("HtmlFileInputTest_one.txt"));
575         assertTrue(response.contains("First"));
576         assertTrue(response.contains("HtmlFileInputTest_two.txt"));
577         assertTrue(response.contains("Second"));
578     }
579 
580 
581     /**
582      * @throws Exception if the test fails
583      */
584     @Test
585     public void webkitdirectory() throws Exception {
586         final Map<String, Class<? extends Servlet>> servlets = new HashMap<>();
587         servlets.put("/upload1", MultipleWebkitdirectoryServlet.class);
588         servlets.put("/upload2", PrintRequestServlet.class);
589         startWebServer("./", null, servlets);
590 
591         final File dir = new File("src/test/resources/pjl-comp-filter");
592         assertTrue(dir.exists());
593         assertTrue(dir.isDirectory());
594 
595         final WebClient client = getWebClient();
596         final HtmlPage firstPage = client.getPage(URL_FIRST + "upload1");
597 
598         final HtmlForm form = firstPage.getForms().get(0);
599         final HtmlFileInput fileInput = form.getInputByName("myInput");
600         fileInput.setDirectory(dir);
601 
602         final HtmlSubmitInput submitInput = form.getInputByValue("Upload");
603         final HtmlPage secondPage = submitInput.click();
604 
605         final String response = secondPage.getWebResponse().getContentAsString();
606 
607         assertTrue(response.contains("index.html"));
608         assertTrue(response.contains("web.xml"));
609         assertTrue(response.contains("pjl-comp-filter-1.8.1.jar"));
610     }
611 
612     /**
613      * Servlet for '/upload1'.
614      */
615     public static class Multiple1Servlet extends HttpServlet {
616 
617         /**
618          * {@inheritDoc}
619          */
620         @Override
621         protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
622             throws ServletException, IOException {
623             response.setCharacterEncoding(UTF_8.name());
624             response.setContentType(MimeType.TEXT_HTML);
625             response.getWriter().write("<html>\n"
626                 + "<body><form action='upload2' method='post' enctype='multipart/form-data'>\n"
627                 + "Name: <input name='myInput' type='file' multiple><br>\n"
628                 + "<input type='submit' value='Upload' id='mySubmit'>\n"
629                 + "</form></body></html>\n");
630         }
631     }
632 
633     /**
634      * Servlet for '/upload1'.
635      */
636     public static class MultipleWebkitdirectoryServlet extends HttpServlet {
637 
638         /**
639          * {@inheritDoc}
640          */
641         @Override
642         protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
643             throws ServletException, IOException {
644             response.setCharacterEncoding(UTF_8.name());
645             response.setContentType(MimeType.TEXT_HTML);
646             response.getWriter().write("<html>\n"
647                 + "<body><form action='upload2' method='post' enctype='multipart/form-data'>\n"
648                 + "Name: <input name='myInput' type='file' multiple webkitdirectory><br>\n"
649                 + "<input type='submit' value='Upload' id='mySubmit'>\n"
650                 + "</form></body></html>\n");
651         }
652     }
653 
654     /**
655      * Prints request content to the response.
656      */
657     public static class PrintRequestServlet extends HttpServlet {
658 
659         /**
660          * {@inheritDoc}
661          */
662         @Override
663         protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
664             throws ServletException, IOException {
665             request.setCharacterEncoding(UTF_8.name());
666             response.setContentType(MimeType.TEXT_HTML);
667             final Writer writer = response.getWriter();
668             final BufferedReader reader = request.getReader();
669             String line;
670             while ((line = reader.readLine()) != null) {
671                 final String normalized = Normalizer.normalize(line, Form.NFD);
672                 writer.write(normalized);
673             }
674         }
675     }
676 
677     /**
678      * @throws Exception if an error occurs
679      */
680     @Test
681     @Alerts("foo, change")
682     public void onchangeMultiple() throws Exception {
683         final String html = DOCTYPE_HTML
684               + "<html>\n"
685               + "<head>\n"
686               + "</head>\n"
687               + "<body>\n"
688               + "  <input type='file' id='f' value='Hello world' multiple"
689               + "      onChange='alert(\"foo\");alert(event.type);'>\n"
690               + "  <button id='clickMe' onclick='test()'>Click Me</button>\n"
691               + "</body></html>";
692 
693         final File pom = new File("pom.xml");
694         final File license = new File("LICENSE.txt");
695 
696         final HtmlPage page = loadPage(html);
697         ((HtmlFileInput) page.getElementById("f")).setFiles(pom, license);
698         Thread.sleep(100);
699         assertEquals(getExpectedAlerts(), getCollectedAlerts(page));
700     }
701 
702     /**
703      * @throws Exception if an error occurs
704      */
705     @Test
706     public void clear() throws Exception {
707         final String html = DOCTYPE_HTML
708                 + "<html><head>\n"
709                 + "  <script>\n"
710                 + "    function test() {\n"
711                 + "      var f =  document.createElement('input');\n"
712                 + "      f.type='file';\n"
713                 + "      f.id='fileId';\n"
714                 + "      document.body.appendChild(f);"
715 
716                 + "      f.value='';\n"
717                 + "    }\n"
718                 + "  </script>\n"
719                 + "</head>\n"
720                 + "<body onload='test()'>\n"
721                 + "</body></html>";
722 
723         final HtmlPage page = loadPage(html);
724         final HtmlFileInput file = page.<HtmlFileInput>getHtmlElementById("fileId");
725         assertEquals(0, file.getFiles().length);
726     }
727 
728     /**
729      * @throws Exception if an error occurs
730      */
731     @Test
732     public void clearFromJava() throws Exception {
733         final String html = DOCTYPE_HTML
734                 + "<html><head>\n"
735                 + "  <script>\n"
736                 + "    function test() {\n"
737                 + "      var f =  document.createElement('input');\n"
738                 + "      f.type='file';\n"
739                 + "      f.id='fileId';\n"
740                 + "      document.body.appendChild(f);"
741                 + "    }\n"
742                 + "  </script>\n"
743                 + "</head>\n"
744                 + "<body onload='test()'>\n"
745                 + "</body></html>";
746 
747         final HtmlPage page = loadPage(html);
748         final HtmlFileInput file = page.<HtmlFileInput>getHtmlElementById("fileId");
749         file.setValue("");
750         assertEquals(0, file.getFiles().length);
751     }
752 }