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 XMLHttpRequestResponseAsXMLEncodingTest 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.responseType = 'document';\n"
156             + "        request.onreadystatechange = () => {\n"
157             + "          if (request.readyState === 4) {\n"
158             + "            let xml = request.response;\n"
159             + "            if (xml == null) { log('null'); return; }\n"
160             + "            log(unicodeEscape(xml.getElementsByTagName('c1')[0].childNodes[0].nodeValue));\n"
161             + "            log(unicodeEscape(xml.getElementsByTagName('c2')[0].childNodes[0].nodeValue));\n"
162             + "            log(unicodeEscape(xml.getElementsByTagName('c3')[0].childNodes[0].nodeValue));\n"
163             + "            log(unicodeEscape(xml.getElementsByTagName('c4')[0].childNodes[0].nodeValue));\n"
164             + "            log(unicodeEscape(xml.getElementsByTagName('c5')[0].childNodes[0].nodeValue));\n"
165             + "          }\n"
166             + "        }\n"
167 
168             + "        request.open('" + httpMethod + "', '" + URL_SECOND + "', true);\n"
169             + "        request.send('');\n"
170             + "      }\n"
171             + "    </script>\n"
172             + "  </head>\n"
173             + "  <body onload='test()'>\n"
174             + LOG_TEXTAREA
175             + "  </body>\n"
176             + "</html>";
177 
178         final String xml = "<?xml version=\"1.0\" " + xmlEnc + "?>"
179                 + "<htmlunit>"
180                 + "<c1>a</c1>"
181                 + "<c2>\u008A\u009A\u00E4\u00A9</c2>"
182                 + "<c3>\u0623\u0647\u0644\u0627\u064B</c3>"
183                 + "<c4>\u043C\u0438\u0440</c4>"
184                 + "<c5>\u623F\u95F4</c5>"
185                 + "</htmlunit>";
186 
187         String[] expected = getExpectedAlerts();
188         if (expected == null || expected.length == 0) {
189             expected = new String[] {"\\u0061", "\\u0160\\u0161\\u00e4\\u00a9", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f", "\\u003f\\u003f"};
190 
191             if (TestMimeType.PLAIN.equals(mimeTypeXml)) {
192                 expected = new String[] {"null"};
193             }
194             else if (TestCharset.UTF8.equals(charsetXmlResponseHeader) || bom != null) {
195                 expected = new String[] {"\\u0061", "\\u008a\\u009a\\u00e4\\u00a9", "\\u0623\\u0647\\u0644\\u0627\\u064b", "\\u043c\\u0438\\u0440", "\\u623f\\u95f4"};
196             }
197             else if (TestMimeType.EMPTY.equals(mimeTypeXml)) {
198                 /* real FF - ignored for the moment
199                 if (getBrowserVersion().isFirefox()
200                         && !TestCharset.UTF8.equals(charsetXmlResponseHeader)) {
201                     expected = new String[] {"null"};
202                 }
203                 else */
204                 if (TestCharset.GB2312.equals(charsetXmlResponseHeader)) {
205                     expected = new String[] {"\\u0061", "\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\ufffd\\u07a7\\u06a7\\ufffd", "\\ufffd\\ufffd\\ufffd\\ufffd"};
206                 }
207                 else if (TestCharset.WINDOWS1250.equals(charsetXmlResponseHeader)) {
208                     expected = new String[] {"\\u0061", "\\u003f\\u003f\\ufffd", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f", "\\u003f\\u003f"};
209                 }
210                 else if (null == charsetXmlResponseHeader || TestCharset.ISO88591.equals(charsetXmlResponseHeader)) {
211                     expected = new String[] {"\\u0061", "\\ufffd\\ufffd\\ufffd", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f", "\\u003f\\u003f"};
212                 }
213             }
214             else if (TestMimeType.XML.equals(mimeTypeXml)) {
215                 if (TestCharset.GB2312.equals(charsetXmlResponseHeader)) {
216                     expected = new String[] {"\\u0061", "\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u043c\\u0438\\u0440", "\\u623f\\u95f4"};
217                 }
218                 else if (TestCharset.WINDOWS1250.equals(charsetXmlResponseHeader)) {
219                     expected = new String[] {"\\u0061", "\\u003f\\u003f\\u00e4\\u00a9", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f", "\\u003f\\u003f"};
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 }