1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.android_webview.test;
6
7import android.test.suitebuilder.annotation.SmallTest;
8import android.util.Pair;
9
10import org.apache.http.Header;
11import org.apache.http.HttpRequest;
12import org.chromium.android_webview.AwContents;
13import org.chromium.android_webview.AwSettings;
14import org.chromium.android_webview.test.util.CommonResources;
15import org.chromium.android_webview.test.util.JSUtils;
16import org.chromium.base.test.util.Feature;
17import org.chromium.content.browser.test.util.CallbackHelper;
18import org.chromium.content.browser.test.util.HistoryUtils;
19import org.chromium.content_public.browser.LoadUrlParams;
20import org.chromium.net.test.util.TestWebServer;
21
22import java.util.ArrayList;
23import java.util.HashMap;
24import java.util.List;
25import java.util.Locale;
26import java.util.Map;
27import java.util.concurrent.TimeUnit;
28
29/**
30 * Test suite for loadUrl().
31 */
32public class LoadUrlTest extends AwTestBase {
33    @SmallTest
34    @Feature({"AndroidWebView"})
35    public void testDataUrl() throws Throwable {
36        final String expectedTitle = "dataUrlTest";
37        final String data =
38            "<html><head><title>" + expectedTitle + "</title></head><body>foo</body></html>";
39
40        final TestAwContentsClient contentsClient = new TestAwContentsClient();
41        final AwTestContainerView testContainerView =
42                createAwTestContainerViewOnMainSync(contentsClient);
43        final AwContents awContents = testContainerView.getAwContents();
44        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(), data,
45                     "text/html", false);
46        assertEquals(expectedTitle, getTitleOnUiThread(awContents));
47    }
48
49    @SmallTest
50    @Feature({"AndroidWebView"})
51    public void testDataUrlBase64() throws Throwable {
52        final String expectedTitle = "dataUrlTestBase64";
53        final String data = "PGh0bWw+PGhlYWQ+PHRpdGxlPmRhdGFVcmxUZXN0QmFzZTY0PC90aXRsZT48" +
54                            "L2hlYWQ+PC9odG1sPg==";
55
56        final TestAwContentsClient contentsClient = new TestAwContentsClient();
57        final AwTestContainerView testContainerView =
58                createAwTestContainerViewOnMainSync(contentsClient);
59        final AwContents awContents = testContainerView.getAwContents();
60        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(), data,
61                     "text/html", true);
62        assertEquals(expectedTitle, getTitleOnUiThread(awContents));
63    }
64
65    @SmallTest
66    @Feature({"AndroidWebView"})
67    public void testDataUrlCharset() throws Throwable {
68        // Note that the \u00a3 (pound sterling) is the important character in the following
69        // string as it's not in the US_ASCII character set.
70        final String expectedTitle = "You win \u00a3100!";
71        final String data =
72            "<html><head><title>" + expectedTitle + "</title></head><body>foo</body></html>";
73        final TestAwContentsClient contentsClient = new TestAwContentsClient();
74        final AwTestContainerView testContainerView =
75                createAwTestContainerViewOnMainSync(contentsClient);
76        final AwContents awContents = testContainerView.getAwContents();
77        loadDataSyncWithCharset(awContents, contentsClient.getOnPageFinishedHelper(), data,
78                     "text/html", false, "UTF-8");
79        assertEquals(expectedTitle, getTitleOnUiThread(awContents));
80    }
81
82    /**
83     * Loads url on the UI thread and blocks until onPageFinished is called.
84     */
85    protected void loadUrlWithExtraHeadersSync(
86            final AwContents awContents,
87            CallbackHelper onPageFinishedHelper,
88            final String url,
89            final Map<String, String> extraHeaders) throws Throwable {
90        int currentCallCount = onPageFinishedHelper.getCallCount();
91        runTestOnUiThread(new Runnable() {
92            @Override
93            public void run() {
94                LoadUrlParams params = new LoadUrlParams(url);
95                params.setExtraHeaders(extraHeaders);
96                awContents.loadUrl(params);
97            }
98        });
99        onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
100                                             TimeUnit.MILLISECONDS);
101    }
102
103    private static List<Pair<String, String>> createHeadersList(String[] namesAndValues) {
104        List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
105        for (int i = 0; i < namesAndValues.length; i += 2)
106            result.add(Pair.create(namesAndValues[i], namesAndValues[i + 1]));
107        return result;
108    }
109
110    private static Map<String, String> createHeadersMap(String[] namesAndValues) {
111        Map<String, String> result = new HashMap<String, String>();
112        for (int i = 0; i < namesAndValues.length; i += 2)
113            result.put(namesAndValues[i], namesAndValues[i + 1]);
114        return result;
115    }
116
117    private void validateRequestHeaders(String[] refNamesAndValues,
118                                        HttpRequest request) {
119        for (int i = 0; i < refNamesAndValues.length; i += 2) {
120            Header[] matchingHeaders = request.getHeaders(refNamesAndValues[i]);
121            assertEquals(1, matchingHeaders.length);
122
123            Header header = matchingHeaders[0];
124            assertEquals(refNamesAndValues[i].toLowerCase(Locale.ENGLISH), header.getName());
125            assertEquals(refNamesAndValues[i + 1], header.getValue());
126        }
127    }
128
129    private void validateNoRequestHeaders(String[] refNamesAndValues,
130                                          HttpRequest request) {
131        for (int i = 0; i < refNamesAndValues.length; i += 2) {
132            Header[] matchingHeaders = request.getHeaders(refNamesAndValues[i]);
133            assertEquals(0, matchingHeaders.length);
134        }
135    }
136
137    @SmallTest
138    @Feature({"AndroidWebView"})
139    public void testLoadUrlWithExtraHeaders() throws Throwable {
140        final TestAwContentsClient contentsClient = new TestAwContentsClient();
141        final AwTestContainerView testContainerView =
142                createAwTestContainerViewOnMainSync(contentsClient);
143        final AwContents awContents = testContainerView.getAwContents();
144
145        TestWebServer webServer = null;
146        try {
147            webServer = new TestWebServer(false);
148            final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
149            webServer.setResponseBase64(imagePath,
150                    CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
151            final String path = "/load_url_with_extra_headers_test.html";
152            final String url = webServer.setResponse(
153                    path,
154                    CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME),
155                    null);
156            String[] extraHeaders = {
157                "X-ExtraHeaders1", "extra-header-data1",
158                "x-extraHeaders2", "EXTRA-HEADER-DATA2"
159            };
160
161            loadUrlWithExtraHeadersSync(awContents,
162                                        contentsClient.getOnPageFinishedHelper(),
163                                        url,
164                                        createHeadersMap(extraHeaders));
165            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
166            // Verify that extra headers are only passed for the main resource.
167            validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(imagePath));
168        } finally {
169            if (webServer != null) webServer.shutdown();
170        }
171    }
172
173    @SmallTest
174    @Feature({"AndroidWebView"})
175    public void testNoOverridingOfExistingHeaders() throws Throwable {
176        final TestAwContentsClient contentsClient = new TestAwContentsClient();
177        final AwTestContainerView testContainerView =
178                createAwTestContainerViewOnMainSync(contentsClient);
179        final AwContents awContents = testContainerView.getAwContents();
180
181        TestWebServer webServer = null;
182        try {
183            webServer = new TestWebServer(false);
184            final String path = "/no_overriding_of_existing_headers_test.html";
185            final String url = webServer.setResponse(
186                    path,
187                    "<html><body>foo</body></html>",
188                    null);
189            String[] extraHeaders = {
190                "user-agent", "007"
191            };
192
193            loadUrlWithExtraHeadersSync(awContents,
194                                        contentsClient.getOnPageFinishedHelper(),
195                                        url,
196                                        createHeadersMap(extraHeaders));
197            Header[] matchingHeaders = webServer.getLastRequest(path).getHeaders(extraHeaders[0]);
198            assertEquals(1, matchingHeaders.length);
199            Header header = matchingHeaders[0];
200            assertEquals(extraHeaders[0].toLowerCase(Locale.ENGLISH),
201                    header.getName().toLowerCase(Locale.ENGLISH));
202            // Just check that the value is there, and it's not the one we provided.
203            assertTrue(header.getValue().length() > 0);
204            assertFalse(extraHeaders[1].equals(header.getValue()));
205        } finally {
206            if (webServer != null) webServer.shutdown();
207        }
208    }
209
210    @SmallTest
211    @Feature({"AndroidWebView"})
212    public void testReloadWithExtraHeaders() throws Throwable {
213        final TestAwContentsClient contentsClient = new TestAwContentsClient();
214        final AwTestContainerView testContainerView =
215                createAwTestContainerViewOnMainSync(contentsClient);
216        final AwContents awContents = testContainerView.getAwContents();
217
218        TestWebServer webServer = null;
219        try {
220            webServer = new TestWebServer(false);
221            final String path = "/reload_with_extra_headers_test.html";
222            final String url = webServer.setResponse(path,
223                    "<html><body>foo</body></html>",
224                    createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
225            String[] extraHeaders = {
226                "X-ExtraHeaders1", "extra-header-data1",
227                "x-extraHeaders2", "EXTRA-HEADER-DATA2"
228            };
229
230            loadUrlWithExtraHeadersSync(awContents,
231                                        contentsClient.getOnPageFinishedHelper(),
232                                        url,
233                                        createHeadersMap(extraHeaders));
234            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
235
236            reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
237            assertEquals(2, webServer.getRequestCount(path));
238            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
239        } finally {
240            if (webServer != null) webServer.shutdown();
241        }
242    }
243
244    @SmallTest
245    @Feature({"AndroidWebView"})
246    public void testRedirectAndReloadWithExtraHeaders() throws Throwable {
247        final TestAwContentsClient contentsClient = new TestAwContentsClient();
248        final AwTestContainerView testContainerView =
249                createAwTestContainerViewOnMainSync(contentsClient);
250        final AwContents awContents = testContainerView.getAwContents();
251
252        TestWebServer webServer = null;
253        try {
254            webServer = new TestWebServer(false);
255            final String path = "/redirect_and_reload_with_extra_headers_test.html";
256            final String url = webServer.setResponse(path,
257                    "<html><body>foo</body></html>",
258                    createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
259            final String redirectedPath = "/redirected.html";
260            final String redirectedUrl = webServer.setRedirect(redirectedPath, path);
261            String[] extraHeaders = {
262                "X-ExtraHeaders1", "extra-header-data1",
263                "x-extraHeaders2", "EXTRA-HEADER-DATA2"
264            };
265
266            loadUrlWithExtraHeadersSync(awContents,
267                                        contentsClient.getOnPageFinishedHelper(),
268                                        redirectedUrl,
269                                        createHeadersMap(extraHeaders));
270            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
271            validateRequestHeaders(extraHeaders, webServer.getLastRequest(redirectedPath));
272
273            // WebView will only reload the main page.
274            reloadSync(awContents, contentsClient.getOnPageFinishedHelper());
275            assertEquals(2, webServer.getRequestCount(path));
276            // No extra headers. This is consistent with legacy behavior.
277            validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(path));
278        } finally {
279            if (webServer != null) webServer.shutdown();
280        }
281    }
282
283    @SmallTest
284    @Feature({"AndroidWebView"})
285    public void testRendererNavigationAndGoBackWithExtraHeaders() throws Throwable {
286        final TestAwContentsClient contentsClient = new TestAwContentsClient();
287        final AwTestContainerView testContainerView =
288                createAwTestContainerViewOnMainSync(contentsClient);
289        final AwContents awContents = testContainerView.getAwContents();
290        final AwSettings settings = getAwSettingsOnUiThread(awContents);
291        settings.setJavaScriptEnabled(true);
292
293        TestWebServer webServer = null;
294        try {
295            webServer = new TestWebServer(false);
296            final String nextPath = "/next.html";
297            final String nextUrl = webServer.setResponse(nextPath,
298                    "<html><body>Next!</body></html>",
299                    null);
300            final String path = "/renderer_nav_and_go_back_with_extra_headers_test.html";
301            final String url = webServer.setResponse(path,
302                    "<html><body><a id=\"next\" href=\"next.html\">Next!</a></body></html>",
303                    createHeadersList(new String[] { "cache-control", "no-cache, no-store" }));
304            String[] extraHeaders = {
305                "X-ExtraHeaders1", "extra-header-data1",
306                "x-extraHeaders2", "EXTRA-HEADER-DATA2"
307            };
308
309            loadUrlWithExtraHeadersSync(awContents,
310                                        contentsClient.getOnPageFinishedHelper(),
311                                        url,
312                                        createHeadersMap(extraHeaders));
313            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
314
315            int currentCallCount = contentsClient.getOnPageFinishedHelper().getCallCount();
316            JSUtils.clickOnLinkUsingJs(this,
317                                       awContents,
318                                       contentsClient.getOnEvaluateJavaScriptResultHelper(),
319                                       "next");
320            contentsClient.getOnPageFinishedHelper().waitForCallback(
321                    currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
322            // No extra headers for the page navigated via clicking.
323            validateNoRequestHeaders(extraHeaders, webServer.getLastRequest(nextPath));
324
325            HistoryUtils.goBackSync(getInstrumentation(),
326                                    awContents.getWebContents(),
327                                    contentsClient.getOnPageFinishedHelper());
328            assertEquals(2, webServer.getRequestCount(path));
329            validateRequestHeaders(extraHeaders, webServer.getLastRequest(path));
330        } finally {
331            if (webServer != null) webServer.shutdown();
332        }
333    }
334}
335