1 /* 2 * Copyright (C) 2009 The Android Open Source Project 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 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.webkit.cts; 18 19 import android.graphics.Bitmap; 20 import android.os.Message; 21 import android.os.SystemClock; 22 import android.test.ActivityInstrumentationTestCase2; 23 import android.view.MotionEvent; 24 import android.view.ViewGroup; 25 import android.webkit.JsPromptResult; 26 import android.webkit.JsResult; 27 import android.webkit.WebIconDatabase; 28 import android.webkit.WebSettings; 29 import android.webkit.WebView; 30 import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient; 31 32 import com.android.compatibility.common.util.NullWebViewUtils; 33 import com.android.compatibility.common.util.PollingCheck; 34 35 public class WebChromeClientTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> { 36 private static final long TEST_TIMEOUT = 5000L; 37 38 private CtsTestServer mWebServer; 39 private WebIconDatabase mIconDb; 40 private WebViewOnUiThread mOnUiThread; 41 private boolean mBlockWindowCreationSync; 42 private boolean mBlockWindowCreationAsync; 43 44 public WebChromeClientTest() { 45 super(WebViewCtsActivity.class); 46 } 47 48 @Override 49 protected void setUp() throws Exception { 50 super.setUp(); 51 WebView webview = getActivity().getWebView(); 52 if (webview != null) { 53 mOnUiThread = new WebViewOnUiThread(this, webview); 54 } 55 mWebServer = new CtsTestServer(getActivity()); 56 } 57 58 @Override 59 protected void tearDown() throws Exception { 60 if (mOnUiThread != null) { 61 mOnUiThread.cleanUp(); 62 } 63 if (mWebServer != null) { 64 mWebServer.shutdown(); 65 } 66 if (mIconDb != null) { 67 mIconDb.removeAllIcons(); 68 mIconDb.close(); 69 } 70 super.tearDown(); 71 } 72 73 public void testOnProgressChanged() { 74 if (!NullWebViewUtils.isWebViewAvailable()) { 75 return; 76 } 77 final MockWebChromeClient webChromeClient = new MockWebChromeClient(); 78 mOnUiThread.setWebChromeClient(webChromeClient); 79 80 assertFalse(webChromeClient.hadOnProgressChanged()); 81 String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); 82 mOnUiThread.loadUrlAndWaitForCompletion(url); 83 84 new PollingCheck(TEST_TIMEOUT) { 85 @Override 86 protected boolean check() { 87 return webChromeClient.hadOnProgressChanged(); 88 } 89 }.run(); 90 } 91 92 public void testOnReceivedTitle() throws Exception { 93 if (!NullWebViewUtils.isWebViewAvailable()) { 94 return; 95 } 96 final MockWebChromeClient webChromeClient = new MockWebChromeClient(); 97 mOnUiThread.setWebChromeClient(webChromeClient); 98 99 assertFalse(webChromeClient.hadOnReceivedTitle()); 100 String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); 101 mOnUiThread.loadUrlAndWaitForCompletion(url); 102 103 new PollingCheck(TEST_TIMEOUT) { 104 @Override 105 protected boolean check() { 106 return webChromeClient.hadOnReceivedTitle(); 107 } 108 }.run(); 109 assertTrue(webChromeClient.hadOnReceivedTitle()); 110 assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, webChromeClient.getPageTitle()); 111 } 112 113 public void testOnReceivedIcon() throws Throwable { 114 if (!NullWebViewUtils.isWebViewAvailable()) { 115 return; 116 } 117 final MockWebChromeClient webChromeClient = new MockWebChromeClient(); 118 mOnUiThread.setWebChromeClient(webChromeClient); 119 120 runTestOnUiThread(new Runnable() { 121 @Override 122 public void run() { 123 // getInstance must run on the UI thread 124 mIconDb = WebIconDatabase.getInstance(); 125 String dbPath = getActivity().getFilesDir().toString() + "/icons"; 126 mIconDb.open(dbPath); 127 } 128 }); 129 getInstrumentation().waitForIdleSync(); 130 Thread.sleep(100); // Wait for open to be received on the icon db thread. 131 132 assertFalse(webChromeClient.hadOnReceivedIcon()); 133 assertNull(mOnUiThread.getFavicon()); 134 135 String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); 136 mOnUiThread.loadUrlAndWaitForCompletion(url); 137 138 new PollingCheck(TEST_TIMEOUT) { 139 @Override 140 protected boolean check() { 141 return webChromeClient.hadOnReceivedIcon(); 142 } 143 }.run(); 144 assertNotNull(mOnUiThread.getFavicon()); 145 } 146 147 public void runWindowTest(boolean expectWindowClose) throws Exception { 148 final MockWebChromeClient webChromeClient = new MockWebChromeClient(); 149 mOnUiThread.setWebChromeClient(webChromeClient); 150 151 final WebSettings settings = mOnUiThread.getSettings(); 152 settings.setJavaScriptEnabled(true); 153 settings.setJavaScriptCanOpenWindowsAutomatically(true); 154 settings.setSupportMultipleWindows(true); 155 156 assertFalse(webChromeClient.hadOnCreateWindow()); 157 158 // Load a page that opens a child window and sets a timeout after which the child 159 // will be closed. 160 mOnUiThread.loadUrlAndWaitForCompletion(mWebServer. 161 getAssetUrl(TestHtmlConstants.JS_WINDOW_URL)); 162 163 new PollingCheck(TEST_TIMEOUT) { 164 @Override 165 protected boolean check() { 166 return webChromeClient.hadOnCreateWindow(); 167 } 168 }.run(); 169 170 if (expectWindowClose) { 171 new PollingCheck(TEST_TIMEOUT) { 172 @Override 173 protected boolean check() { 174 return webChromeClient.hadOnCloseWindow(); 175 } 176 }.run(); 177 } else { 178 assertFalse(webChromeClient.hadOnCloseWindow()); 179 } 180 } 181 public void testWindows() throws Exception { 182 if (!NullWebViewUtils.isWebViewAvailable()) { 183 return; 184 } 185 runWindowTest(true); 186 } 187 188 public void testBlockWindowsSync() throws Exception { 189 if (!NullWebViewUtils.isWebViewAvailable()) { 190 return; 191 } 192 mBlockWindowCreationSync = true; 193 runWindowTest(false); 194 } 195 196 public void testBlockWindowsAsync() throws Exception { 197 if (!NullWebViewUtils.isWebViewAvailable()) { 198 return; 199 } 200 mBlockWindowCreationAsync = true; 201 runWindowTest(false); 202 } 203 204 public void testOnJsBeforeUnloadIsCalled() throws Exception { 205 if (!NullWebViewUtils.isWebViewAvailable()) { 206 return; 207 } 208 209 final MockWebChromeClient webChromeClient = new MockWebChromeClient(); 210 mOnUiThread.setWebChromeClient(webChromeClient); 211 212 final WebSettings settings = mOnUiThread.getSettings(); 213 settings.setJavaScriptEnabled(true); 214 settings.setJavaScriptCanOpenWindowsAutomatically(true); 215 216 assertFalse(webChromeClient.hadOnJsBeforeUnload()); 217 218 mOnUiThread.loadUrlAndWaitForCompletion(mWebServer.getAssetUrl(TestHtmlConstants.JS_UNLOAD_URL)); 219 220 // Send a user gesture, required for unload to execute since WebView version 60. 221 tapWebView(); 222 223 // unload should trigger when we try to navigate away 224 mOnUiThread.loadUrlAndWaitForCompletion(mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL)); 225 226 new PollingCheck(TEST_TIMEOUT) { 227 @Override 228 protected boolean check() { 229 return webChromeClient.hadOnJsBeforeUnload(); 230 } 231 }.run(); 232 } 233 234 public void testOnJsAlert() throws Exception { 235 if (!NullWebViewUtils.isWebViewAvailable()) { 236 return; 237 } 238 final MockWebChromeClient webChromeClient = new MockWebChromeClient(); 239 mOnUiThread.setWebChromeClient(webChromeClient); 240 241 final WebSettings settings = mOnUiThread.getSettings(); 242 settings.setJavaScriptEnabled(true); 243 settings.setJavaScriptCanOpenWindowsAutomatically(true); 244 245 assertFalse(webChromeClient.hadOnJsAlert()); 246 247 String url = mWebServer.getAssetUrl(TestHtmlConstants.JS_ALERT_URL); 248 mOnUiThread.loadUrlAndWaitForCompletion(url); 249 250 new PollingCheck(TEST_TIMEOUT) { 251 @Override 252 protected boolean check() { 253 return webChromeClient.hadOnJsAlert(); 254 } 255 }.run(); 256 assertEquals(webChromeClient.getMessage(), "testOnJsAlert"); 257 } 258 259 public void testOnJsConfirm() throws Exception { 260 if (!NullWebViewUtils.isWebViewAvailable()) { 261 return; 262 } 263 final MockWebChromeClient webChromeClient = new MockWebChromeClient(); 264 mOnUiThread.setWebChromeClient(webChromeClient); 265 266 final WebSettings settings = mOnUiThread.getSettings(); 267 settings.setJavaScriptEnabled(true); 268 settings.setJavaScriptCanOpenWindowsAutomatically(true); 269 270 assertFalse(webChromeClient.hadOnJsConfirm()); 271 272 String url = mWebServer.getAssetUrl(TestHtmlConstants.JS_CONFIRM_URL); 273 mOnUiThread.loadUrlAndWaitForCompletion(url); 274 275 new PollingCheck(TEST_TIMEOUT) { 276 @Override 277 protected boolean check() { 278 return webChromeClient.hadOnJsConfirm(); 279 } 280 }.run(); 281 assertEquals(webChromeClient.getMessage(), "testOnJsConfirm"); 282 } 283 284 public void testOnJsPrompt() throws Exception { 285 if (!NullWebViewUtils.isWebViewAvailable()) { 286 return; 287 } 288 final MockWebChromeClient webChromeClient = new MockWebChromeClient(); 289 mOnUiThread.setWebChromeClient(webChromeClient); 290 291 final WebSettings settings = mOnUiThread.getSettings(); 292 settings.setJavaScriptEnabled(true); 293 settings.setJavaScriptCanOpenWindowsAutomatically(true); 294 295 assertFalse(webChromeClient.hadOnJsPrompt()); 296 297 final String promptResult = "CTS"; 298 webChromeClient.setPromptResult(promptResult); 299 String url = mWebServer.getAssetUrl(TestHtmlConstants.JS_PROMPT_URL); 300 mOnUiThread.loadUrlAndWaitForCompletion(url); 301 302 new PollingCheck(TEST_TIMEOUT) { 303 @Override 304 protected boolean check() { 305 return webChromeClient.hadOnJsPrompt(); 306 } 307 }.run(); 308 // the result returned by the client gets set as the page title 309 new PollingCheck(TEST_TIMEOUT) { 310 @Override 311 protected boolean check() { 312 return mOnUiThread.getTitle().equals(promptResult); 313 } 314 }.run(); 315 assertEquals(webChromeClient.getMessage(), "testOnJsPrompt"); 316 } 317 318 /** 319 * Taps in the the center of a webview. 320 */ 321 private void tapWebView() { 322 int[] location = mOnUiThread.getLocationOnScreen(); 323 int middleX = location[0] + mOnUiThread.getWebView().getWidth() / 2; 324 int middleY = location[1] + mOnUiThread.getWebView().getHeight() / 2; 325 326 long timeDown = SystemClock.uptimeMillis(); 327 getInstrumentation().sendPointerSync( 328 MotionEvent.obtain(timeDown, timeDown, MotionEvent.ACTION_DOWN, 329 middleX, middleY, 0)); 330 331 long timeUp = SystemClock.uptimeMillis(); 332 getInstrumentation().sendPointerSync( 333 MotionEvent.obtain(timeUp, timeUp, MotionEvent.ACTION_UP, 334 middleX, middleY, 0)); 335 336 // Wait for the system to process all events in the queue 337 getInstrumentation().waitForIdleSync(); 338 } 339 340 private class MockWebChromeClient extends WaitForProgressClient { 341 private boolean mHadOnProgressChanged; 342 private boolean mHadOnReceivedTitle; 343 private String mPageTitle; 344 private boolean mHadOnJsAlert; 345 private boolean mHadOnJsConfirm; 346 private boolean mHadOnJsPrompt; 347 private boolean mHadOnJsBeforeUnload; 348 private String mMessage; 349 private String mPromptResult; 350 private boolean mHadOnCloseWindow; 351 private boolean mHadOnCreateWindow; 352 private boolean mHadOnRequestFocus; 353 private boolean mHadOnReceivedIcon; 354 355 public MockWebChromeClient() { 356 super(mOnUiThread); 357 } 358 359 public void setPromptResult(String promptResult) { 360 mPromptResult = promptResult; 361 } 362 363 public boolean hadOnProgressChanged() { 364 return mHadOnProgressChanged; 365 } 366 367 public boolean hadOnReceivedTitle() { 368 return mHadOnReceivedTitle; 369 } 370 371 public String getPageTitle() { 372 return mPageTitle; 373 } 374 375 public boolean hadOnJsAlert() { 376 return mHadOnJsAlert; 377 } 378 379 public boolean hadOnJsConfirm() { 380 return mHadOnJsConfirm; 381 } 382 383 public boolean hadOnJsPrompt() { 384 return mHadOnJsPrompt; 385 } 386 387 public boolean hadOnJsBeforeUnload() { 388 return mHadOnJsBeforeUnload; 389 } 390 391 public boolean hadOnCreateWindow() { 392 return mHadOnCreateWindow; 393 } 394 395 public boolean hadOnCloseWindow() { 396 return mHadOnCloseWindow; 397 } 398 399 public boolean hadOnRequestFocus() { 400 return mHadOnRequestFocus; 401 } 402 403 public boolean hadOnReceivedIcon() { 404 return mHadOnReceivedIcon; 405 } 406 407 public String getMessage() { 408 return mMessage; 409 } 410 411 @Override 412 public void onProgressChanged(WebView view, int newProgress) { 413 super.onProgressChanged(view, newProgress); 414 mHadOnProgressChanged = true; 415 } 416 417 @Override 418 public void onReceivedTitle(WebView view, String title) { 419 super.onReceivedTitle(view, title); 420 mPageTitle = title; 421 mHadOnReceivedTitle = true; 422 } 423 424 @Override 425 public boolean onJsAlert(WebView view, String url, String message, JsResult result) { 426 super.onJsAlert(view, url, message, result); 427 mHadOnJsAlert = true; 428 mMessage = message; 429 result.confirm(); 430 return true; 431 } 432 433 @Override 434 public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { 435 super.onJsConfirm(view, url, message, result); 436 mHadOnJsConfirm = true; 437 mMessage = message; 438 result.confirm(); 439 return true; 440 } 441 442 @Override 443 public boolean onJsPrompt(WebView view, String url, String message, 444 String defaultValue, JsPromptResult result) { 445 super.onJsPrompt(view, url, message, defaultValue, result); 446 mHadOnJsPrompt = true; 447 mMessage = message; 448 result.confirm(mPromptResult); 449 return true; 450 } 451 452 @Override 453 public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) { 454 super.onJsBeforeUnload(view, url, message, result); 455 mHadOnJsBeforeUnload = true; 456 mMessage = message; 457 result.confirm(); 458 return true; 459 } 460 461 @Override 462 public void onCloseWindow(WebView window) { 463 super.onCloseWindow(window); 464 mHadOnCloseWindow = true; 465 } 466 467 @Override 468 public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture, 469 Message resultMsg) { 470 mHadOnCreateWindow = true; 471 if (mBlockWindowCreationSync) { 472 return false; 473 } 474 WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; 475 if (mBlockWindowCreationAsync) { 476 transport.setWebView(null); 477 } else { 478 WebView childView = new WebView(getActivity()); 479 final WebSettings settings = childView.getSettings(); 480 settings.setJavaScriptEnabled(true); 481 childView.setWebChromeClient(this); 482 transport.setWebView(childView); 483 getActivity().addContentView(childView, new ViewGroup.LayoutParams( 484 ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 485 } 486 resultMsg.sendToTarget(); 487 return true; 488 } 489 490 @Override 491 public void onRequestFocus(WebView view) { 492 mHadOnRequestFocus = true; 493 } 494 495 @Override 496 public void onReceivedIcon(WebView view, Bitmap icon) { 497 mHadOnReceivedIcon = true; 498 } 499 } 500 } 501