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 5package org.chromium.android_webview.test; 6 7import android.test.FlakyTest; 8import android.test.suitebuilder.annotation.MediumTest; 9import android.test.suitebuilder.annotation.SmallTest; 10 11import org.chromium.android_webview.AwContents; 12import org.chromium.android_webview.test.util.CommonResources; 13import org.chromium.base.ThreadUtils; 14import org.chromium.base.test.util.DisabledTest; 15import org.chromium.base.test.util.Feature; 16import org.chromium.content.browser.NavigationEntry; 17import org.chromium.content.browser.NavigationHistory; 18import org.chromium.content.browser.test.util.HistoryUtils; 19import org.chromium.content.browser.test.util.TestCallbackHelperContainer; 20import org.chromium.net.test.util.TestWebServer; 21 22import java.util.concurrent.Callable; 23 24public 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} 310