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 XMLHttpRequestResponseTextEncodingTest 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.open('" + httpMethod + "', '" + URL_SECOND + "', false);\n"
156             + "        request.send('');\n"
157             + "        log(unicodeEscape(request.responseText));\n"
158             + "      }\n"
159             + "    </script>\n"
160             + "  </head>\n"
161             + "  <body onload='test()'>\n"
162             + LOG_TEXTAREA
163             + "  </body>\n"
164             + "</html>";
165 
166         final String xml = "<?xml version=\"1.0\" " + xmlEnc + "?>"
167                 + "<htmlunit>"
168                 + "<c1>a</c1>"
169                 + "<c2>\u008A\u009A\u00E4\u00A9</c2>"
170                 + "<c3>\u0623\u0647\u0644\u0627\u064B</c3>"
171                 + "<c4>\u043C\u0438\u0440</c4>"
172                 + "<c5>\u623F\u95F4</c5>"
173                 + "</htmlunit>";
174 
175         String[] expected = getExpectedAlerts();
176         if (expected == null || expected.length == 0) {
177             expected = new String[] {
178                 escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
179                 + "\\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"};
180 
181             if (TestCharset.UTF8.equals(charsetXmlResponseHeader) || bom != null) {
182                 expected = new String[] {
183                     escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
184                     + "\\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"};
185             }
186             else if (TestMimeType.EMPTY.equals(mimeTypeXml)) {
187                 if (TestCharset.GB2312.equals(charsetXmlResponseHeader)) {
188                     expected = new String[] {
189                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
190                         + "\\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"};
191                 }
192                 else if (TestCharset.WINDOWS1250.equals(charsetXmlResponseHeader)) {
193                     expected = new String[] {
194                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
195                         + "\\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"};
196                 }
197                 else if (null == charsetXmlResponseHeader || TestCharset.ISO88591.equals(charsetXmlResponseHeader)) {
198                     expected = new String[] {
199                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
200                         + "\\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"};
201                 }
202             }
203             else if (TestMimeType.PLAIN.equals(mimeTypeXml) || TestMimeType.XML.equals(mimeTypeXml)) {
204                 if (TestCharset.GB2312.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\\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"};
208                 }
209                 else if (TestCharset.WINDOWS1250.equals(charsetXmlResponseHeader)) {
210                     expected = new String[] {
211                         escape("<?xml version=\"1.0\" " + xmlEnc + "?>")
212                         + "\\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"};
213                 }
214             }
215         }
216 
217         setupXmlResponse(xml, bom, mimeTypeXml, charsetXmlResponseHeader);
218         final WebDriver driver = loadPage2(html, URL_FIRST, MimeType.TEXT_HTML,
219                 charsetHtmlResponseHeader == null ? null : charsetHtmlResponseHeader.getCharset());
220 
221         verifyTextArea2(driver, expected);
222     }
223 }