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.util.MimeType;
23  import org.junit.jupiter.params.ParameterizedTest;
24  import org.junit.jupiter.params.provider.Arguments;
25  import org.junit.jupiter.params.provider.MethodSource;
26  import org.openqa.selenium.WebDriver;
27  
28  /**
29   * Tests for {@link XMLHttpRequest} encoding.
30   *
31   * @author Ronald Brill
32   */
33  public class XMLHttpRequestResponseXMLEncodingTest extends AbstractXMLHttpRequestEncodingTest {
34  
35      /**
36       * Returns the parameterized data.
37       * @return the parameterized data
38       * @throws Exception if an error occurs
39       */
40      public static Collection<Arguments> data() throws Exception {
41          final List<Arguments> list = new ArrayList<>();
42  
43          final String[] xmlEncodingHeaders = {"", "utf8"};
44          final TestCharset[] charsetHtmlResponseHeaders =
45                  new TestCharset[] {null, TestCharset.UTF8, TestCharset.ISO88591, TestCharset.WINDOWS1250, TestCharset.GB2312};
46          final TestMimeType[] mimeTypeXmls = {TestMimeType.EMPTY, TestMimeType.XML, TestMimeType.PLAIN};
47          final TestCharset[] charsetXmlResponseHeaders =
48                  new TestCharset[] {null, TestCharset.UTF8, TestCharset.ISO88591, TestCharset.WINDOWS1250, TestCharset.GB2312};
49          final String[] boms = {null, BOM_UTF_8, BOM_UTF_16LE, BOM_UTF_16BE};
50  
51          for (final Object xmlEncodingHeader : xmlEncodingHeaders) {
52              for (final Object charsetHtmlResponseHeader : charsetHtmlResponseHeaders) {
53                  for (final Object mimeTypeXml : mimeTypeXmls) {
54                      for (final Object charsetXmlResponseHeader : charsetXmlResponseHeaders) {
55                          for (final Object bom : boms) {
56                              list.add(Arguments.of(xmlEncodingHeader,
57                                                     charsetHtmlResponseHeader,
58                                                     mimeTypeXml,
59                                                     charsetXmlResponseHeader,
60                                                     bom));
61                          }
62                      }
63                  }
64              }
65          }
66  
67          return list;
68      }
69  
70      /**
71       * The default test.
72       * @throws Exception if an error occurs
73       */
74      @ParameterizedTest(name = "_{0}_{1}_{2}_{3}_{4}")
75      @MethodSource("data")
76      void responseText(
77             final String xmlEncodingHeader,
78              final TestCharset charsetHtmlResponseHeader,
79              final TestMimeType mimeTypeXml,
80              final TestCharset charsetXmlResponseHeader,
81              final String bom) throws Exception {
82          responseText("GET", xmlEncodingHeader, charsetHtmlResponseHeader, mimeTypeXml, charsetXmlResponseHeader, bom);
83          responseText("POST", xmlEncodingHeader, charsetHtmlResponseHeader, mimeTypeXml, charsetXmlResponseHeader, bom);
84      }
85  
86      private void responseText(
87              final String httpMethod,
88              final String xmlEncodingHeader,
89              final TestCharset charsetHtmlResponseHeader,
90              final TestMimeType mimeTypeXml,
91              final TestCharset charsetXmlResponseHeader,
92              final String bom) throws Exception {
93  
94          String xmlEnc = xmlEncodingHeader;
95          if ("utf8".equals(xmlEnc)) {
96              xmlEnc = "encoding=\"utf-8\"";
97          }
98  
99          final String html = DOCTYPE_HTML
100             + "<html>\n"
101             + "  <head>\n"
102             + "    <script>\n"
103             + LOG_TEXTAREA_FUNCTION
104 
105             + "      function unicodeEscape(str) {\n"
106             + "        let result = '', index = 0, charCode, escape;\n"
107             + "        while (!isNaN(charCode = str.charCodeAt(index++))) {\n"
108             + "          escape = charCode.toString(16);\n"
109             + "          result += '\\\\u' + ('0000' + escape).slice(-4);\n"
110             + "        }\n"
111             + "        return result;\n"
112             + "      }\n"
113 
114             + "      function test() {\n"
115             + "        var request = new XMLHttpRequest();\n"
116             + "        request.open('" + httpMethod + "', '" + URL_SECOND + "', false);\n"
117             + "        request.send('');\n"
118             + "        let xml = request.responseXML;\n"
119             + "        if (xml== null) { log('null'); return; }\n"
120 
121             + "        log(unicodeEscape(xml.getElementsByTagName('c1')[0].childNodes[0].nodeValue));\n"
122             + "        log(unicodeEscape(xml.getElementsByTagName('c2')[0].childNodes[0].nodeValue));\n"
123             + "        log(unicodeEscape(xml.getElementsByTagName('c3')[0].childNodes[0].nodeValue));\n"
124             + "        log(unicodeEscape(xml.getElementsByTagName('c4')[0].childNodes[0].nodeValue));\n"
125             + "        log(unicodeEscape(xml.getElementsByTagName('c5')[0].childNodes[0].nodeValue));\n"
126             + "      }\n"
127             + "    </script>\n"
128             + "  </head>\n"
129             + "  <body onload='test()'>\n"
130             + LOG_TEXTAREA
131             + "  </body>\n"
132             + "</html>";
133 
134         final String xml = "<?xml version=\"1.0\" " + xmlEnc + "?>"
135                 + "<htmlunit>"
136                 + "<c1>a</c1>"
137                 + "<c2>\u008A\u009A\u00E4\u00A9</c2>"
138                 + "<c3>\u0623\u0647\u0644\u0627\u064B</c3>"
139                 + "<c4>\u043C\u0438\u0440</c4>"
140                 + "<c5>\u623F\u95F4</c5>"
141                 + "</htmlunit>";
142 
143         String[] expected = getExpectedAlerts();
144         if (expected == null || expected.length == 0) {
145             expected = new String[] {"\\u0061", "\\u0160\\u0161\\u00e4\\u00a9", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f", "\\u003f\\u003f"};
146 
147             if (TestMimeType.PLAIN.equals(mimeTypeXml)) {
148                 expected = new String[] {"null"};
149             }
150             else if (TestCharset.UTF8.equals(charsetXmlResponseHeader) || bom != null) {
151                 expected = new String[] {"\\u0061", "\\u008a\\u009a\\u00e4\\u00a9", "\\u0623\\u0647\\u0644\\u0627\\u064b", "\\u043c\\u0438\\u0440", "\\u623f\\u95f4"};
152             }
153             else if (TestMimeType.EMPTY.equals(mimeTypeXml)) {
154                 /* real FF - ignored for the moment
155                 if (getBrowserVersion().isFirefox()
156                         && !TestCharset.UTF8.equals(charsetXmlResponseHeader)) {
157                     expected = new String[] {"null"};
158                 }
159                 else */
160                 if (TestCharset.GB2312.equals(charsetXmlResponseHeader)) {
161                     expected = new String[] {"\\u0061", "\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\ufffd\\u07a7\\u06a7\\ufffd", "\\ufffd\\ufffd\\ufffd\\ufffd"};
162                 }
163                 else if (TestCharset.WINDOWS1250.equals(charsetXmlResponseHeader)) {
164                     expected = new String[] {"\\u0061", "\\u003f\\u003f\\ufffd", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f", "\\u003f\\u003f"};
165                 }
166                 else if (null == charsetXmlResponseHeader || TestCharset.ISO88591.equals(charsetXmlResponseHeader)) {
167                     expected = new String[] {"\\u0061", "\\ufffd\\ufffd\\ufffd", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f", "\\u003f\\u003f"};
168                 }
169             }
170             else if (TestMimeType.XML.equals(mimeTypeXml)) {
171                 if (TestCharset.GB2312.equals(charsetXmlResponseHeader)) {
172                     expected = new String[] {"\\u0061", "\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u043c\\u0438\\u0440", "\\u623f\\u95f4"};
173                 }
174                 else if (TestCharset.WINDOWS1250.equals(charsetXmlResponseHeader)) {
175                     expected = new String[] {"\\u0061", "\\u003f\\u003f\\u00e4\\u00a9", "\\u003f\\u003f\\u003f\\u003f\\u003f", "\\u003f\\u003f\\u003f", "\\u003f\\u003f"};
176                 }
177             }
178         }
179 
180         setupXmlResponse(xml, bom, mimeTypeXml, charsetXmlResponseHeader);
181         final WebDriver driver = loadPage2(html, URL_FIRST, MimeType.TEXT_HTML,
182                 charsetHtmlResponseHeader == null ? null : charsetHtmlResponseHeader.getCharset());
183 
184         verifyTextArea2(driver, expected);
185     }
186 }