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.encoding;
16  
17  import java.util.ArrayList;
18  import java.util.Collection;
19  import java.util.List;
20  
21  import org.htmlunit.javascript.host.xml.XMLHttpRequest;
22  import org.htmlunit.junit.BrowserParameterizedRunner;
23  import org.htmlunit.junit.BrowserParameterizedRunner.Default;
24  import org.htmlunit.util.MimeType;
25  import org.junit.Test;
26  import org.junit.runner.RunWith;
27  import org.junit.runners.Parameterized.Parameter;
28  import org.junit.runners.Parameterized.Parameters;
29  import org.openqa.selenium.WebDriver;
30  
31  /**
32   * Tests for {@link XMLHttpRequest} encoding.
33   *
34   * @author Ronald Brill
35   */
36  @RunWith(BrowserParameterizedRunner.class)
37  public class XMLHttpRequestResponseAsTextEncodingTest extends AbstractXMLHttpRequestEncodingTest {
38  
39      /**
40       * Returns the parameterized data.
41       * @return the parameterized data
42       * @throws Exception if an error occurs
43       */
44      @Parameters
45      public static Collection<Object[]> data() throws Exception {
46          final List<Object[]> list = new ArrayList<>();
47  
48          final String[] xmlEncodingHeaders = {"", "utf8"};
49          final TestCharset[] charsetHtmlResponseHeaders =
50                  new TestCharset[] {null, TestCharset.UTF8, TestCharset.ISO88591, TestCharset.WINDOWS1250, TestCharset.GB2312};
51          final TestMimeType[] mimeTypeXmls = {TestMimeType.EMPTY, TestMimeType.XML, TestMimeType.PLAIN};
52          final TestCharset[] charsetXmlResponseHeaders =
53                  new TestCharset[] {null, TestCharset.UTF8, TestCharset.ISO88591, TestCharset.WINDOWS1250, TestCharset.GB2312};
54          final String[] boms = {null, BOM_UTF_8, BOM_UTF_16LE, BOM_UTF_16BE};
55  
56          for (final Object xmlEncodingHeader : xmlEncodingHeaders) {
57              for (final Object charsetHtmlResponseHeader : charsetHtmlResponseHeaders) {
58                  for (final Object mimeTypeXml : mimeTypeXmls) {
59                      for (final Object charsetXmlResponseHeader : charsetXmlResponseHeaders) {
60                          for (final Object bom : boms) {
61                              list.add(new Object[] {xmlEncodingHeader,
62                                                     charsetHtmlResponseHeader,
63                                                     mimeTypeXml,
64                                                     charsetXmlResponseHeader,
65                                                     bom});
66                          }
67                      }
68                  }
69              }
70          }
71  
72          return list;
73      }
74  
75      /**
76       * The xmlEncodingHeader.
77       */
78      @Parameter
79      public String xmlEncodingHeader_;
80  
81      /**
82       * The charsetHtmlResponseHeader.
83       */
84      @Parameter(1)
85      public TestCharset charsetHtmlResponseHeader_;
86  
87      /**
88       * The charsetHtmlResponseHeader.
89       */
90      @Parameter(2)
91      public TestMimeType mimeTypeXml_;
92  
93      /**
94       * The charsetXmlResponseHeader.
95       */
96      @Parameter(3)
97      public TestCharset charsetXmlResponseHeader_;
98  
99      /**
100      * The charsetXmlResponseHeader.
101      */
102     @Parameter(4)
103     public String bom_;
104 
105     /**
106      * The default test.
107      * @throws Exception if an error occurs
108      */
109     @Test
110     @Default
111     public void responseText() throws Exception {
112         responseText(xmlEncodingHeader_, charsetHtmlResponseHeader_, mimeTypeXml_, charsetXmlResponseHeader_, bom_);
113     }
114 
115     private void responseText(
116            final String xmlEncodingHeader,
117             final TestCharset charsetHtmlResponseHeader,
118             final TestMimeType mimeTypeXml,
119             final TestCharset charsetXmlResponseHeader,
120             final String bom) throws Exception {
121         responseText("GET", xmlEncodingHeader, charsetHtmlResponseHeader, mimeTypeXml, charsetXmlResponseHeader, bom);
122         responseText("POST", xmlEncodingHeader, charsetHtmlResponseHeader, mimeTypeXml, charsetXmlResponseHeader, bom);
123     }
124 
125     private void responseText(
126             final String httpMethod,
127             final String xmlEncodingHeader,
128             final TestCharset charsetHtmlResponseHeader,
129             final TestMimeType mimeTypeXml,
130             final TestCharset charsetXmlResponseHeader,
131             final String bom) throws Exception {
132 
133         String xmlEnc = xmlEncodingHeader;
134         if ("utf8".equals(xmlEnc)) {
135             xmlEnc = "encoding=\"utf-8\"";
136         }
137 
138         final String html = DOCTYPE_HTML
139             + "<html>\n"
140             + "  <head>\n"
141             + "    <script>\n"
142             + LOG_TEXTAREA_FUNCTION
143 
144             + "      function unicodeEscape(str) {\n"
145             + "        let result = '', index = 0, charCode, escape;\n"
146             + "        while (!isNaN(charCode = str.charCodeAt(index++))) {\n"
147             + "          escape = charCode.toString(16);\n"
148             + "          result += '\\\\u' + ('0000' + escape).slice(-4);\n"
149             + "        }\n"
150             + "        return result;\n"
151             + "      }\n"
152 
153             + "      function test() {\n"
154             + "        var request = new XMLHttpRequest();\n"
155             + "        request.onreadystatechange = () => {\n"
156             + "          if (request.readyState === 4) {\n"
157             + "            let txt = request.response;\n"
158             + "            if (txt == null) { log('null'); return; }\n"
159             + "            log(unicodeEscape(txt));\n"
160             + "          }\n"
161             + "        }\n"
162 
163             + "        request.open('" + httpMethod + "', '" + URL_SECOND + "', true);\n"
164             + "        request.send('');\n"
165             + "      }\n"
166             + "    </script>\n"
167             + "  </head>\n"
168             + "  <body onload='test()'>\n"
169             + LOG_TEXTAREA
170             + "  </body>\n"
171             + "</html>";
172 
173         final String xml = "<?xml version=\"1.0\" " + xmlEnc + "?>"
174                 + "<htmlunit>"
175                 + "<c1>a</c1>"
176                 + "<c2>\u008A\u009A\u00E4\u00A9</c2>"
177                 + "<c3>\u0623\u0647\u0644\u0627\u064B</c3>"
178                 + "<c4>\u043C\u0438\u0440</c4>"
179                 + "<c5>\u623F\u95F4</c5>"
180                 + "</htmlunit>";
181 
182         String[] expected = getExpectedAlerts();
183         if (expected == null || expected.length == 0) {
184             expected = new String[] {
185                 escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
186                 + "\\u003c\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e\\u003c\\u0063\\u0031\\u003e\\u0061\\u003c\\u002f\\u0063\\u0031\\u003e\\u003c\\u0063\\u0032\\u003e\\u0160\\u0161\\u00e4\\u00a9\\u003c\\u002f\\u0063\\u0032\\u003e\\u003c\\u0063\\u0033\\u003e\\u003f\\u003f\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0033\\u003e\\u003c\\u0063\\u0034\\u003e\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0034\\u003e\\u003c\\u0063\\u0035\\u003e\\u003f\\u003f\\u003c\\u002f\\u0063\\u0035\\u003e\\u003c\\u002f\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e"};
187 
188             if (TestCharset.UTF8.equals(charsetXmlResponseHeader) || bom != null) {
189                 expected = new String[] {
190                     escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
191                     + "\\u003c\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e\\u003c\\u0063\\u0031\\u003e\\u0061\\u003c\\u002f\\u0063\\u0031\\u003e\\u003c\\u0063\\u0032\\u003e\\u008a\\u009a\\u00e4\\u00a9\\u003c\\u002f\\u0063\\u0032\\u003e\\u003c\\u0063\\u0033\\u003e\\u0623\\u0647\\u0644\\u0627\\u064b\\u003c\\u002f\\u0063\\u0033\\u003e\\u003c\\u0063\\u0034\\u003e\\u043c\\u0438\\u0440\\u003c\\u002f\\u0063\\u0034\\u003e\\u003c\\u0063\\u0035\\u003e\\u623f\\u95f4\\u003c\\u002f\\u0063\\u0035\\u003e\\u003c\\u002f\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e"};
192             }
193             else if (TestMimeType.EMPTY.equals(mimeTypeXml)) {
194                 if (TestCharset.GB2312.equals(charsetXmlResponseHeader)) {
195                     expected = new String[] {
196                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
197                         + "\\u003c\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e\\u003c\\u0063\\u0031\\u003e\\u0061\\u003c\\u002f\\u0063\\u0031\\u003e\\u003c\\u0063\\u0032\\u003e\\u003f\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0032\\u003e\\u003c\\u0063\\u0033\\u003e\\u003f\\u003f\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0033\\u003e\\u003c\\u0063\\u0034\\u003e\\ufffd\\u07a7\\u06a7\\ufffd\\u003c\\u002f\\u0063\\u0034\\u003e\\u003c\\u0063\\u0035\\u003e\\ufffd\\ufffd\\ufffd\\ufffd\\u003c\\u002f\\u0063\\u0035\\u003e\\u003c\\u002f\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e"};
198                 }
199                 else if (TestCharset.WINDOWS1250.equals(charsetXmlResponseHeader)) {
200                     expected = new String[] {
201                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
202                         + "\\u003c\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e\\u003c\\u0063\\u0031\\u003e\\u0061\\u003c\\u002f\\u0063\\u0031\\u003e\\u003c\\u0063\\u0032\\u003e\\u003f\\u003f\\ufffd\\u003c\\u002f\\u0063\\u0032\\u003e\\u003c\\u0063\\u0033\\u003e\\u003f\\u003f\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0033\\u003e\\u003c\\u0063\\u0034\\u003e\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0034\\u003e\\u003c\\u0063\\u0035\\u003e\\u003f\\u003f\\u003c\\u002f\\u0063\\u0035\\u003e\\u003c\\u002f\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e"};
203                 }
204                 else if (null == charsetXmlResponseHeader || TestCharset.ISO88591.equals(charsetXmlResponseHeader)) {
205                     expected = new String[] {
206                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
207                         + "\\u003c\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e\\u003c\\u0063\\u0031\\u003e\\u0061\\u003c\\u002f\\u0063\\u0031\\u003e\\u003c\\u0063\\u0032\\u003e\\ufffd\\ufffd\\ufffd\\u003c\\u002f\\u0063\\u0032\\u003e\\u003c\\u0063\\u0033\\u003e\\u003f\\u003f\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0033\\u003e\\u003c\\u0063\\u0034\\u003e\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0034\\u003e\\u003c\\u0063\\u0035\\u003e\\u003f\\u003f\\u003c\\u002f\\u0063\\u0035\\u003e\\u003c\\u002f\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e"};
208                 }
209             }
210             else if (TestMimeType.PLAIN.equals(mimeTypeXml) || TestMimeType.XML.equals(mimeTypeXml)) {
211                 if (TestCharset.GB2312.equals(charsetXmlResponseHeader)) {
212                     expected = new String[] {
213                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
214                         + "\\u003c\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e\\u003c\\u0063\\u0031\\u003e\\u0061\\u003c\\u002f\\u0063\\u0031\\u003e\\u003c\\u0063\\u0032\\u003e\\u003f\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0032\\u003e\\u003c\\u0063\\u0033\\u003e\\u003f\\u003f\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0033\\u003e\\u003c\\u0063\\u0034\\u003e\\u043c\\u0438\\u0440\\u003c\\u002f\\u0063\\u0034\\u003e\\u003c\\u0063\\u0035\\u003e\\u623f\\u95f4\\u003c\\u002f\\u0063\\u0035\\u003e\\u003c\\u002f\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e"};
215                 }
216                 else if (TestCharset.WINDOWS1250.equals(charsetXmlResponseHeader)) {
217                     expected = new String[] {
218                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
219                         + "\\u003c\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e\\u003c\\u0063\\u0031\\u003e\\u0061\\u003c\\u002f\\u0063\\u0031\\u003e\\u003c\\u0063\\u0032\\u003e\\u003f\\u003f\\u00e4\\u00a9\\u003c\\u002f\\u0063\\u0032\\u003e\\u003c\\u0063\\u0033\\u003e\\u003f\\u003f\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0033\\u003e\\u003c\\u0063\\u0034\\u003e\\u003f\\u003f\\u003f\\u003c\\u002f\\u0063\\u0034\\u003e\\u003c\\u0063\\u0035\\u003e\\u003f\\u003f\\u003c\\u002f\\u0063\\u0035\\u003e\\u003c\\u002f\\u0068\\u0074\\u006d\\u006c\\u0075\\u006e\\u0069\\u0074\\u003e"};
220                 }
221             }
222         }
223 
224         setupXmlResponse(xml, bom, mimeTypeXml, charsetXmlResponseHeader);
225         final WebDriver driver = loadPage2(html, URL_FIRST, MimeType.TEXT_HTML,
226                 charsetHtmlResponseHeader == null ? null : charsetHtmlResponseHeader.getCharset());
227 
228         verifyTextArea2(driver, expected);
229     }
230 }