1 | // Copyright (c) 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 | |
5 | package org.chromium.android_webview.test; |
6 | |
7 | import android.test.FlakyTest; |
8 | import android.test.suitebuilder.annotation.MediumTest; |
9 | import android.test.suitebuilder.annotation.SmallTest; |
10 | |
11 | import org.chromium.android_webview.AwContents; |
12 | import org.chromium.android_webview.test.util.CommonResources; |
13 | import org.chromium.base.ThreadUtils; |
14 | import org.chromium.base.test.util.DisabledTest; |
15 | import org.chromium.base.test.util.Feature; |
16 | import org.chromium.content.browser.NavigationEntry; |
17 | import org.chromium.content.browser.NavigationHistory; |
18 | import org.chromium.content.browser.test.util.HistoryUtils; |
19 | import org.chromium.content.browser.test.util.TestCallbackHelperContainer; |
20 | import org.chromium.net.test.util.TestWebServer; |
21 | |
22 | import java.util.concurrent.Callable; |
23 | |
24 | public class NavigationHistoryTest extends AwTestBase { |
25 | |
26 | private static final String PAGE_1_PATH = "/page1.html"; |
27 | private static final String PAGE_1_TITLE = "Page 1 Title"; |
28 | private static final String PAGE_2_PATH = "/page2.html"; |
29 | private static final String PAGE_2_TITLE = "Page 2 Title"; |
30 | private static final String PAGE_WITH_HASHTAG_REDIRECT_TITLE = "Page with hashtag"; |
31 | private static final String LOGIN_PAGE_PATH = "/login.html"; |
32 | private static final String LOGIN_PAGE_TITLE = "Login page"; |
33 | private static final String LOGIN_RESPONSE_PAGE_PATH = "/login-response.html"; |
34 | private static final String LOGIN_RESPONSE_PAGE_TITLE = "Login response"; |
35 | private static final String LOGIN_RESPONSE_PAGE_HELP_LINK_ID = "help"; |
36 | |
37 | private TestWebServer mWebServer; |
38 | private TestAwContentsClient mContentsClient; |
39 | private AwContents mAwContents; |
40 | |
41 | @Override |
42 | public void setUp() throws Exception { |
43 | super.setUp(); |
44 | mContentsClient = new TestAwContentsClient(); |
45 | final AwTestContainerView testContainerView = |
46 | createAwTestContainerViewOnMainSync(mContentsClient); |
47 | mAwContents = testContainerView.getAwContents(); |
48 | mWebServer = new TestWebServer(false); |
49 | } |
50 | |
51 | @Override |
52 | public void tearDown() throws Exception { |
53 | mWebServer.shutdown(); |
54 | super.tearDown(); |
55 | } |
56 | |
57 | private NavigationHistory getNavigationHistory(final AwContents awContents) |
58 | throws Exception { |
59 | return ThreadUtils.runOnUiThreadBlocking(new Callable<NavigationHistory>() { |
60 | @Override |
61 | public NavigationHistory call() { |
62 | return awContents.getContentViewCore().getNavigationHistory(); |
63 | } |
64 | }); |
65 | } |
66 | |
67 | private void checkHistoryItem(NavigationEntry item, String url, String originalUrl, |
68 | String title, boolean faviconNull) { |
69 | assertEquals(url, item.getUrl()); |
70 | assertEquals(originalUrl, item.getOriginalUrl()); |
71 | assertEquals(title, item.getTitle()); |
72 | if (faviconNull) { |
73 | assertNull(item.getFavicon()); |
74 | } else { |
75 | assertNotNull(item.getFavicon()); |
76 | } |
77 | } |
78 | |
79 | private String addPage1ToServer(TestWebServer webServer) { |
80 | return mWebServer.setResponse(PAGE_1_PATH, |
81 | CommonResources.makeHtmlPageFrom( |
82 | "<title>" + PAGE_1_TITLE + "</title>", |
83 | "<div>This is test page 1.</div>"), |
84 | CommonResources.getTextHtmlHeaders(false)); |
85 | } |
86 | |
87 | private String addPage2ToServer(TestWebServer webServer) { |
88 | return mWebServer.setResponse(PAGE_2_PATH, |
89 | CommonResources.makeHtmlPageFrom( |
90 | "<title>" + PAGE_2_TITLE + "</title>", |
91 | "<div>This is test page 2.</div>"), |
92 | CommonResources.getTextHtmlHeaders(false)); |
93 | } |
94 | |
95 | private String addPageWithHashTagRedirectToServer(TestWebServer webServer) { |
96 | return mWebServer.setResponse(PAGE_2_PATH, |
97 | CommonResources.makeHtmlPageFrom( |
98 | "<title>" + PAGE_WITH_HASHTAG_REDIRECT_TITLE + "</title>", |
99 | "<iframe onLoad=\"location.replace(location.href + '#tag');\" />"), |
100 | CommonResources.getTextHtmlHeaders(false)); |
101 | } |
102 | |
103 | @SmallTest |
104 | public void testNavigateOneUrl() throws Throwable { |
105 | NavigationHistory history = getNavigationHistory(mAwContents); |
106 | assertEquals(0, history.getEntryCount()); |
107 | |
108 | final String pageWithHashTagRedirectUrl = addPageWithHashTagRedirectToServer(mWebServer); |
109 | enableJavaScriptOnUiThread(mAwContents); |
110 | |
111 | loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), |
112 | pageWithHashTagRedirectUrl); |
113 | |
114 | history = getNavigationHistory(mAwContents); |
115 | checkHistoryItem(history.getEntryAtIndex(0), |
116 | pageWithHashTagRedirectUrl + "#tag", |
117 | pageWithHashTagRedirectUrl, |
118 | PAGE_WITH_HASHTAG_REDIRECT_TITLE, |
119 | true); |
120 | |
121 | assertEquals(0, history.getCurrentEntryIndex()); |
122 | } |
123 | |
124 | @SmallTest |
125 | public void testNavigateTwoUrls() throws Throwable { |
126 | NavigationHistory list = getNavigationHistory(mAwContents); |
127 | assertEquals(0, list.getEntryCount()); |
128 | |
129 | final TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = |
130 | mContentsClient.getOnPageFinishedHelper(); |
131 | final String page1Url = addPage1ToServer(mWebServer); |
132 | final String page2Url = addPage2ToServer(mWebServer); |
133 | |
134 | loadUrlSync(mAwContents, onPageFinishedHelper, page1Url); |
135 | loadUrlSync(mAwContents, onPageFinishedHelper, page2Url); |
136 | |
137 | list = getNavigationHistory(mAwContents); |
138 | |
139 | // Make sure there is a new entry entry the list |
140 | assertEquals(2, list.getEntryCount()); |
141 | |
142 | // Make sure the first entry is still okay |
143 | checkHistoryItem(list.getEntryAtIndex(0), |
144 | page1Url, |
145 | page1Url, |
146 | PAGE_1_TITLE, |
147 | true); |
148 | |
149 | // Make sure the second entry was added properly |
150 | checkHistoryItem(list.getEntryAtIndex(1), |
151 | page2Url, |
152 | page2Url, |
153 | PAGE_2_TITLE, |
154 | true); |
155 | |
156 | assertEquals(1, list.getCurrentEntryIndex()); |
157 | |
158 | } |
159 | |
160 | @SmallTest |
161 | public void testNavigateTwoUrlsAndBack() throws Throwable { |
162 | final TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = |
163 | mContentsClient.getOnPageFinishedHelper(); |
164 | NavigationHistory list = getNavigationHistory(mAwContents); |
165 | assertEquals(0, list.getEntryCount()); |
166 | |
167 | final String page1Url = addPage1ToServer(mWebServer); |
168 | final String page2Url = addPage2ToServer(mWebServer); |
169 | |
170 | loadUrlSync(mAwContents, onPageFinishedHelper, page1Url); |
171 | loadUrlSync(mAwContents, onPageFinishedHelper, page2Url); |
172 | |
173 | HistoryUtils.goBackSync(getInstrumentation(), mAwContents.getContentViewCore(), |
174 | onPageFinishedHelper); |
175 | list = getNavigationHistory(mAwContents); |
176 | |
177 | // Make sure the first entry is still okay |
178 | checkHistoryItem(list.getEntryAtIndex(0), |
179 | page1Url, |
180 | page1Url, |
181 | PAGE_1_TITLE, |
182 | true); |
183 | |
184 | // Make sure the second entry is still okay |
185 | checkHistoryItem(list.getEntryAtIndex(1), |
186 | page2Url, |
187 | page2Url, |
188 | PAGE_2_TITLE, |
189 | true); |
190 | |
191 | // Make sure the current index is back to 0 |
192 | assertEquals(0, list.getCurrentEntryIndex()); |
193 | } |
194 | |
195 | /** |
196 | * Disabled until favicons are getting fetched when using ContentView. |
197 | * |
198 | * @SmallTest |
199 | * @throws Throwable |
200 | */ |
201 | @DisabledTest |
202 | public void testFavicon() throws Throwable { |
203 | NavigationHistory list = getNavigationHistory(mAwContents); |
204 | |
205 | mWebServer.setResponseBase64("/" + CommonResources.FAVICON_FILENAME, |
206 | CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(false)); |
207 | final String url = mWebServer.setResponse("/favicon.html", |
208 | CommonResources.FAVICON_STATIC_HTML, null); |
209 | |
210 | assertEquals(0, list.getEntryCount()); |
211 | getAwSettingsOnUiThread(mAwContents).setImagesEnabled(true); |
212 | loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url); |
213 | |
214 | list = getNavigationHistory(mAwContents); |
215 | |
216 | // Make sure the first entry is still okay. |
217 | checkHistoryItem(list.getEntryAtIndex(0), url, url, "", false); |
218 | } |
219 | |
220 | private String addNoncacheableLoginPageToServer(TestWebServer webServer) { |
221 | final String submitButtonId = "submit"; |
222 | final String loginPageHtml = |
223 | "<html>" + |
224 | " <head>" + |
225 | " <title>" + LOGIN_PAGE_TITLE + "</title>" + |
226 | " <script>" + |
227 | " function startAction() {" + |
228 | " button = document.getElementById('" + submitButtonId + "');" + |
229 | " button.click();" + |
230 | " }" + |
231 | " </script>" + |
232 | " </head>" + |
233 | " <body onload='setTimeout(startAction, 0)'>" + |
234 | " <form action='" + LOGIN_RESPONSE_PAGE_PATH.substring(1) + "' method='post'>" + |
235 | " <input type='text' name='login'>" + |
236 | " <input id='" + submitButtonId + "' type='submit' value='Submit'>" + |
237 | " </form>" + |
238 | " </body>" + |
239 | "</html>"; |
240 | return mWebServer.setResponse(LOGIN_PAGE_PATH, |
241 | loginPageHtml, |
242 | CommonResources.getTextHtmlHeaders(true)); |
243 | } |
244 | |
245 | private String addNoncacheableLoginResponsePageToServer(TestWebServer webServer) { |
246 | final String loginResponsePageHtml = |
247 | "<html>" + |
248 | " <head>" + |
249 | " <title>" + LOGIN_RESPONSE_PAGE_TITLE + "</title>" + |
250 | " </head>" + |
251 | " <body>" + |
252 | " Login incorrect" + |
253 | " <div><a id='" + LOGIN_RESPONSE_PAGE_HELP_LINK_ID + "' href='" + |
254 | PAGE_1_PATH.substring(1) + "'>Help</a></div>'" + |
255 | " </body>" + |
256 | "</html>"; |
257 | return mWebServer.setResponse(LOGIN_RESPONSE_PAGE_PATH, |
258 | loginResponsePageHtml, |
259 | CommonResources.getTextHtmlHeaders(true)); |
260 | } |
261 | |
262 | // This test simulates Google login page behavior. The page is non-cacheable |
263 | // and uses POST method for submission. It also contains a help link, leading |
264 | // to another page. We are verifying that it is possible to go back to the |
265 | // submitted login page after visiting the help page. |
266 | @MediumTest |
267 | @Feature({"AndroidWebView"}) |
268 | public void testNavigateBackToNoncacheableLoginPage() throws Throwable { |
269 | final TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = |
270 | mContentsClient.getOnPageFinishedHelper(); |
271 | |
272 | final String loginPageUrl = addNoncacheableLoginPageToServer(mWebServer); |
273 | final String loginResponsePageUrl = addNoncacheableLoginResponsePageToServer(mWebServer); |
274 | final String page1Url = addPage1ToServer(mWebServer); |
275 | |
276 | getAwSettingsOnUiThread(mAwContents).setJavaScriptEnabled(true); |
277 | loadUrlSync(mAwContents, onPageFinishedHelper, loginPageUrl); |
278 | // Since the page performs an async action, we can't rely on callbacks. |
279 | assertTrue(pollOnUiThread(new Callable<Boolean>() { |
280 | @Override |
281 | public Boolean call() { |
282 | String title = mAwContents.getContentViewCore().getTitle(); |
283 | return LOGIN_RESPONSE_PAGE_TITLE.equals(title); |
284 | } |
285 | })); |
286 | executeJavaScriptAndWaitForResult(mAwContents, |
287 | mContentsClient, |
288 | "link = document.getElementById('" + LOGIN_RESPONSE_PAGE_HELP_LINK_ID + "');" + |
289 | "link.click();"); |
290 | assertTrue(pollOnUiThread(new Callable<Boolean>() { |
291 | @Override |
292 | public Boolean call() { |
293 | String title = mAwContents.getContentViewCore().getTitle(); |
294 | return PAGE_1_TITLE.equals(title); |
295 | } |
296 | })); |
297 | // Verify that we can still go back to the login response page despite that |
298 | // it is non-cacheable. |
299 | HistoryUtils.goBackSync(getInstrumentation(), mAwContents.getContentViewCore(), |
300 | onPageFinishedHelper); |
301 | assertTrue(pollOnUiThread(new Callable<Boolean>() { |
302 | @Override |
303 | public Boolean call() { |
304 | String title = mAwContents.getContentViewCore().getTitle(); |
305 | return LOGIN_RESPONSE_PAGE_TITLE.equals(title); |
306 | } |
307 | })); |
308 | } |
309 | } |