TestShellActivity.java revision 3ae8c42152d890ab771053fa6b16b038ee44326d
1/* 2 * Copyright (C) 2007 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 17package com.android.dumprendertree; 18 19import com.android.dumprendertree.forwarder.ForwardService; 20 21import android.app.Activity; 22import android.app.AlertDialog; 23import android.content.Context; 24import android.content.DialogInterface; 25import android.content.DialogInterface.OnClickListener; 26import android.content.Intent; 27import android.graphics.Bitmap; 28import android.net.http.SslError; 29import android.os.Bundle; 30import android.os.Environment; 31import android.os.Handler; 32import android.os.Message; 33import android.util.Log; 34import android.view.ViewGroup; 35import android.view.Window; 36import android.webkit.ConsoleMessage; 37import android.webkit.CookieManager; 38import android.webkit.GeolocationPermissions; 39import android.webkit.HttpAuthHandler; 40import android.webkit.JsPromptResult; 41import android.webkit.JsResult; 42import android.webkit.SslErrorHandler; 43import android.webkit.WebChromeClient; 44import android.webkit.WebSettings; 45import android.webkit.WebSettingsClassic; 46import android.webkit.WebStorage; 47import android.webkit.WebView; 48import android.webkit.WebViewClassic; 49import android.webkit.WebViewClient; 50import android.widget.LinearLayout; 51 52import java.io.BufferedReader; 53import java.io.File; 54import java.io.FileOutputStream; 55import java.io.FileReader; 56import java.io.IOException; 57import java.net.MalformedURLException; 58import java.net.URL; 59import java.util.HashMap; 60import java.util.Iterator; 61import java.util.Map; 62import java.util.Vector; 63 64public class TestShellActivity extends Activity implements LayoutTestController { 65 66 static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP} 67 68 // String constants for use with layoutTestController.overridePreferences 69 private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED = 70 "WebKitOfflineWebApplicationCacheEnabled"; 71 private final String WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY = "WebKitUsesPageCachePreferenceKey"; 72 73 public class AsyncHandler extends Handler { 74 @Override 75 public void handleMessage(Message msg) { 76 if (msg.what == MSG_TIMEOUT) { 77 mTimedOut = true; 78 mWebView.stopLoading(); 79 if (mCallback != null) 80 mCallback.timedOut(mWebView.getUrl()); 81 if (!mRequestedWebKitData) { 82 requestWebKitData(); 83 } else { 84 // if timed out and webkit data has been dumped before 85 // finish directly 86 finished(); 87 } 88 return; 89 } else if (msg.what == MSG_WEBKIT_DATA) { 90 Log.v(LOGTAG, "Received WebView dump data"); 91 mHandler.removeMessages(MSG_DUMP_TIMEOUT); 92 TestShellActivity.this.dump(mTimedOut, (String)msg.obj); 93 return; 94 } else if (msg.what == MSG_DUMP_TIMEOUT) { 95 throw new RuntimeException("WebView dump timeout, is it pegged?"); 96 } 97 super.handleMessage(msg); 98 } 99 } 100 101 public void requestWebKitData() { 102 setDumpTimeout(DUMP_TIMEOUT_MS); 103 Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA); 104 105 if (mRequestedWebKitData) 106 throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl()); 107 108 mRequestedWebKitData = true; 109 Log.v(LOGTAG, "message sent to WebView to dump text."); 110 switch (mDumpDataType) { 111 case DUMP_AS_TEXT: 112 callback.arg1 = mDumpTopFrameAsText ? 1 : 0; 113 callback.arg2 = mDumpChildFramesAsText ? 1 : 0; 114 mWebViewClassic.documentAsText(callback); 115 break; 116 case EXT_REPR: 117 mWebViewClassic.externalRepresentation(callback); 118 break; 119 default: 120 finished(); 121 break; 122 } 123 } 124 125 private void setDumpTimeout(long timeout) { 126 Log.v(LOGTAG, "setting dump timeout at " + timeout); 127 Message msg = mHandler.obtainMessage(MSG_DUMP_TIMEOUT); 128 mHandler.sendMessageDelayed(msg, timeout); 129 } 130 131 public void clearCache() { 132 mWebView.freeMemory(); 133 } 134 135 @Override 136 protected void onCreate(Bundle icicle) { 137 super.onCreate(icicle); 138 requestWindowFeature(Window.FEATURE_PROGRESS); 139 140 LinearLayout contentView = new LinearLayout(this); 141 contentView.setOrientation(LinearLayout.VERTICAL); 142 setContentView(contentView); 143 144 CookieManager.setAcceptFileSchemeCookies(true); 145 mWebView = new WebView(this); 146 mWebViewClassic = WebViewClassic.fromWebView(mWebView); 147 mEventSender = new WebViewEventSender(mWebView); 148 mCallbackProxy = new CallbackProxy(mEventSender, this); 149 150 mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController"); 151 mWebView.addJavascriptInterface(mCallbackProxy, "eventSender"); 152 setupWebViewForLayoutTests(mWebView, mCallbackProxy); 153 154 contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0f)); 155 156 mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); 157 158 // Expose window.gc function to JavaScript. JSC build exposes 159 // this function by default, but V8 requires the flag to turn it on. 160 // WebView::setJsFlags is noop in JSC build. 161 mWebViewClassic.setJsFlags("--expose_gc"); 162 163 mHandler = new AsyncHandler(); 164 165 Intent intent = getIntent(); 166 if (intent != null) { 167 executeIntent(intent); 168 } 169 170 // This is asynchronous, but it gets processed by WebCore before it starts loading pages. 171 mWebViewClassic.useMockDeviceOrientation(); 172 } 173 174 @Override 175 protected void onNewIntent(Intent intent) { 176 super.onNewIntent(intent); 177 executeIntent(intent); 178 } 179 180 private void executeIntent(Intent intent) { 181 resetTestStatus(); 182 if (!Intent.ACTION_VIEW.equals(intent.getAction())) { 183 return; 184 } 185 186 mTotalTestCount = intent.getIntExtra(TOTAL_TEST_COUNT, mTotalTestCount); 187 mCurrentTestNumber = intent.getIntExtra(CURRENT_TEST_NUMBER, mCurrentTestNumber); 188 189 mTestUrl = intent.getStringExtra(TEST_URL); 190 if (mTestUrl == null) { 191 mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST); 192 if(mUiAutoTestPath != null) { 193 beginUiAutoTest(); 194 } 195 return; 196 } 197 198 mResultFile = intent.getStringExtra(RESULT_FILE); 199 mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0); 200 mStopOnRefError = intent.getBooleanExtra(STOP_ON_REF_ERROR, false); 201 setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount); 202 float ratio = (float)mCurrentTestNumber / mTotalTestCount; 203 int progress = (int)(ratio * Window.PROGRESS_END); 204 getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress); 205 206 Log.v(LOGTAG, " Loading " + mTestUrl); 207 208 if (mTestUrl.contains("/dumpAsText/")) { 209 dumpAsText(false); 210 } 211 212 mWebView.loadUrl(mTestUrl); 213 214 if (mTimeoutInMillis > 0) { 215 // Create a timeout timer 216 Message m = mHandler.obtainMessage(MSG_TIMEOUT); 217 mHandler.sendMessageDelayed(m, mTimeoutInMillis); 218 } 219 } 220 221 private void beginUiAutoTest() { 222 try { 223 mTestListReader = new BufferedReader( 224 new FileReader(mUiAutoTestPath)); 225 } catch (IOException ioe) { 226 Log.e(LOGTAG, "Failed to open test list for read.", ioe); 227 finishUiAutoTest(); 228 return; 229 } 230 moveToNextTest(); 231 } 232 233 private void finishUiAutoTest() { 234 try { 235 if(mTestListReader != null) 236 mTestListReader.close(); 237 } catch (IOException ioe) { 238 Log.w(LOGTAG, "Failed to close test list file.", ioe); 239 } 240 ForwardService.getForwardService().stopForwardService(); 241 finished(); 242 } 243 244 private void moveToNextTest() { 245 String url = null; 246 try { 247 url = mTestListReader.readLine(); 248 } catch (IOException ioe) { 249 Log.e(LOGTAG, "Failed to read next test.", ioe); 250 finishUiAutoTest(); 251 return; 252 } 253 if (url == null) { 254 mUiAutoTestPath = null; 255 finishUiAutoTest(); 256 AlertDialog.Builder builder = new AlertDialog.Builder(this); 257 builder.setMessage("All tests finished. Exit?") 258 .setCancelable(false) 259 .setPositiveButton("Yes", new OnClickListener(){ 260 public void onClick(DialogInterface dialog, int which) { 261 TestShellActivity.this.finish(); 262 } 263 }) 264 .setNegativeButton("No", new OnClickListener(){ 265 public void onClick(DialogInterface dialog, int which) { 266 dialog.cancel(); 267 } 268 }); 269 builder.create().show(); 270 return; 271 } 272 Intent intent = new Intent(Intent.ACTION_VIEW); 273 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 274 intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url)); 275 intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, ++mCurrentTestNumber); 276 intent.putExtra(TIMEOUT_IN_MILLIS, 10000); 277 executeIntent(intent); 278 } 279 280 @Override 281 protected void onStop() { 282 super.onStop(); 283 mWebView.stopLoading(); 284 } 285 286 @Override 287 protected void onDestroy() { 288 super.onDestroy(); 289 mWebView.destroy(); 290 mWebView = null; 291 mWebViewClassic = null; 292 } 293 294 @Override 295 public void onLowMemory() { 296 super.onLowMemory(); 297 Log.e(LOGTAG, "Low memory, clearing caches"); 298 mWebView.freeMemory(); 299 } 300 301 // Dump the page 302 public void dump(boolean timeout, String webkitData) { 303 mDumpWebKitData = true; 304 if (mResultFile == null || mResultFile.length() == 0) { 305 finished(); 306 return; 307 } 308 309 if (mCallback != null) { 310 mCallback.dumpResult(webkitData); 311 } 312 313 try { 314 File parentDir = new File(mResultFile).getParentFile(); 315 if (!parentDir.exists()) { 316 parentDir.mkdirs(); 317 } 318 319 FileOutputStream os = new FileOutputStream(mResultFile); 320 if (timeout) { 321 Log.w("Layout test: Timeout", mResultFile); 322 os.write(TIMEOUT_STR.getBytes()); 323 os.write('\n'); 324 } 325 if (mDumpTitleChanges) 326 os.write(mTitleChanges.toString().getBytes()); 327 if (mDialogStrings != null) 328 os.write(mDialogStrings.toString().getBytes()); 329 mDialogStrings = null; 330 if (mDatabaseCallbackStrings != null) 331 os.write(mDatabaseCallbackStrings.toString().getBytes()); 332 mDatabaseCallbackStrings = null; 333 if (mConsoleMessages != null) 334 os.write(mConsoleMessages.toString().getBytes()); 335 mConsoleMessages = null; 336 if (webkitData != null) 337 os.write(webkitData.getBytes()); 338 os.flush(); 339 os.close(); 340 } catch (IOException ex) { 341 Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage()); 342 } 343 344 finished(); 345 } 346 347 public void setCallback(TestShellCallback callback) { 348 mCallback = callback; 349 } 350 351 public boolean finished() { 352 if (canMoveToNextTest()) { 353 mHandler.removeMessages(MSG_TIMEOUT); 354 if (mUiAutoTestPath != null) { 355 //don't really finish here 356 moveToNextTest(); 357 } else { 358 if (mCallback != null) { 359 mCallback.finished(); 360 } 361 } 362 return true; 363 } 364 return false; 365 } 366 367 public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) { 368 mDefaultDumpDataType = defaultDumpDataType; 369 } 370 371 // ....................................... 372 // LayoutTestController Functions 373 public void dumpAsText(boolean enablePixelTests) { 374 // Added after webkit update to r63859. See trac.webkit.org/changeset/63730. 375 if (enablePixelTests) { 376 Log.v(LOGTAG, "dumpAsText(enablePixelTests == true) not implemented on Android!"); 377 } 378 379 mDumpDataType = DumpDataType.DUMP_AS_TEXT; 380 mDumpTopFrameAsText = true; 381 if (mWebView != null) { 382 String url = mWebView.getUrl(); 383 Log.v(LOGTAG, "dumpAsText called: "+url); 384 } 385 } 386 387 public void dumpChildFramesAsText() { 388 mDumpDataType = DumpDataType.DUMP_AS_TEXT; 389 mDumpChildFramesAsText = true; 390 if (mWebView != null) { 391 String url = mWebView.getUrl(); 392 Log.v(LOGTAG, "dumpChildFramesAsText called: "+url); 393 } 394 } 395 396 public void waitUntilDone() { 397 mWaitUntilDone = true; 398 String url = mWebView.getUrl(); 399 Log.v(LOGTAG, "waitUntilDone called: " + url); 400 } 401 402 public void notifyDone() { 403 String url = mWebView.getUrl(); 404 Log.v(LOGTAG, "notifyDone called: " + url); 405 if (mWaitUntilDone) { 406 mWaitUntilDone = false; 407 if (!mRequestedWebKitData && !mTimedOut && !finished()) { 408 requestWebKitData(); 409 } 410 } 411 } 412 413 public void display() { 414 mWebView.invalidate(); 415 } 416 417 public void clearBackForwardList() { 418 mWebView.clearHistory(); 419 420 } 421 422 public void dumpBackForwardList() { 423 //printf("\n============== Back Forward List ==============\n"); 424 // mWebHistory 425 //printf("===============================================\n"); 426 427 } 428 429 public void dumpChildFrameScrollPositions() { 430 // TODO Auto-generated method stub 431 432 } 433 434 public void dumpEditingCallbacks() { 435 // TODO Auto-generated method stub 436 437 } 438 439 public void dumpSelectionRect() { 440 // TODO Auto-generated method stub 441 442 } 443 444 public void dumpTitleChanges() { 445 if (!mDumpTitleChanges) { 446 mTitleChanges = new StringBuffer(); 447 } 448 mDumpTitleChanges = true; 449 } 450 451 public void keepWebHistory() { 452 if (!mKeepWebHistory) { 453 mWebHistory = new Vector(); 454 } 455 mKeepWebHistory = true; 456 } 457 458 public void queueBackNavigation(int howfar) { 459 // TODO Auto-generated method stub 460 461 } 462 463 public void queueForwardNavigation(int howfar) { 464 // TODO Auto-generated method stub 465 466 } 467 468 public void queueLoad(String Url, String frameTarget) { 469 // TODO Auto-generated method stub 470 471 } 472 473 public void queueReload() { 474 mWebView.reload(); 475 } 476 477 public void queueScript(String scriptToRunInCurrentContext) { 478 mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); 479 } 480 481 public void repaintSweepHorizontally() { 482 // TODO Auto-generated method stub 483 484 } 485 486 public void setAcceptsEditing(boolean b) { 487 // TODO Auto-generated method stub 488 489 } 490 491 public void setMainFrameIsFirstResponder(boolean b) { 492 // TODO Auto-generated method stub 493 494 } 495 496 public void setWindowIsKey(boolean b) { 497 // This is meant to show/hide the window. The best I can find 498 // is setEnabled() 499 mWebView.setEnabled(b); 500 } 501 502 public void testRepaint() { 503 mWebView.invalidate(); 504 } 505 506 public void dumpDatabaseCallbacks() { 507 Log.v(LOGTAG, "dumpDatabaseCallbacks called."); 508 mDumpDatabaseCallbacks = true; 509 } 510 511 public void setCanOpenWindows() { 512 Log.v(LOGTAG, "setCanOpenWindows called."); 513 mCanOpenWindows = true; 514 } 515 516 /** 517 * Sets the Geolocation permission state to be used for all future requests. 518 */ 519 public void setGeolocationPermission(boolean allow) { 520 mIsGeolocationPermissionSet = true; 521 mGeolocationPermission = allow; 522 523 if (mPendingGeolocationPermissionCallbacks != null) { 524 Iterator iter = mPendingGeolocationPermissionCallbacks.keySet().iterator(); 525 while (iter.hasNext()) { 526 GeolocationPermissions.Callback callback = 527 (GeolocationPermissions.Callback) iter.next(); 528 String origin = (String) mPendingGeolocationPermissionCallbacks.get(callback); 529 callback.invoke(origin, mGeolocationPermission, false); 530 } 531 mPendingGeolocationPermissionCallbacks = null; 532 } 533 } 534 535 public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, 536 boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { 537 WebViewClassic.fromWebView(mWebView).setMockDeviceOrientation(canProvideAlpha, alpha, 538 canProvideBeta, beta, canProvideGamma, gamma); 539 } 540 541 public void overridePreference(String key, boolean value) { 542 // TODO: We should look up the correct WebView for the frame which 543 // called the layoutTestController method. Currently, we just use the 544 // WebView for the main frame. EventSender suffers from the same 545 // problem. 546 if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) { 547 mWebViewClassic.getSettings().setAppCacheEnabled(value); 548 } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) { 549 // Cache the maximum possible number of pages. 550 mWebViewClassic.getSettings().setPageCacheCapacity(Integer.MAX_VALUE); 551 } else { 552 Log.w(LOGTAG, "LayoutTestController.overridePreference(): " + 553 "Unsupported preference '" + key + "'"); 554 } 555 } 556 557 public void setXSSAuditorEnabled (boolean flag) { 558 mWebViewClassic.getSettings().setXSSAuditorEnabled(flag); 559 } 560 561 private final WebViewClient mViewClient = new WebViewClient(){ 562 @Override 563 public void onPageFinished(WebView view, String url) { 564 Log.v(LOGTAG, "onPageFinished, url=" + url); 565 mPageFinished = true; 566 567 // Calling finished() will check if we've met all the conditions for completing 568 // this test and move to the next one if we are ready. Otherwise we ask WebCore to 569 // dump the page. 570 if (finished()) { 571 return; 572 } 573 574 if (!mWaitUntilDone && !mRequestedWebKitData && !mTimedOut) { 575 requestWebKitData(); 576 } else { 577 if (mWaitUntilDone) { 578 Log.v(LOGTAG, "page finished loading but waiting for notifyDone to be called: " + url); 579 } 580 581 if (mRequestedWebKitData) { 582 Log.v(LOGTAG, "page finished loading but webkit data has already been requested: " + url); 583 } 584 585 if (mTimedOut) { 586 Log.v(LOGTAG, "page finished loading but already timed out: " + url); 587 } 588 } 589 590 super.onPageFinished(view, url); 591 } 592 593 @Override 594 public void onPageStarted(WebView view, String url, Bitmap favicon) { 595 Log.v(LOGTAG, "onPageStarted, url=" + url); 596 mPageFinished = false; 597 super.onPageStarted(view, url, favicon); 598 } 599 600 @Override 601 public void onReceivedError(WebView view, int errorCode, String description, 602 String failingUrl) { 603 Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode 604 + ", desc=" + description + ", url=" + failingUrl); 605 super.onReceivedError(view, errorCode, description, failingUrl); 606 } 607 608 @Override 609 public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, 610 String host, String realm) { 611 if (handler.useHttpAuthUsernamePassword() && view != null) { 612 String[] credentials = view.getHttpAuthUsernamePassword(host, realm); 613 if (credentials != null && credentials.length == 2) { 614 handler.proceed(credentials[0], credentials[1]); 615 return; 616 } 617 } 618 handler.cancel(); 619 } 620 621 @Override 622 public void onReceivedSslError(WebView view, SslErrorHandler handler, 623 SslError error) { 624 handler.proceed(); 625 } 626 }; 627 628 629 private final WebChromeClient mChromeClient = new WebChromeClient() { 630 @Override 631 public void onReceivedTitle(WebView view, String title) { 632 setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount + ": "+ title); 633 if (mDumpTitleChanges) { 634 mTitleChanges.append("TITLE CHANGED: "); 635 mTitleChanges.append(title); 636 mTitleChanges.append("\n"); 637 } 638 } 639 640 @Override 641 public boolean onJsAlert(WebView view, String url, String message, 642 JsResult result) { 643 if (mDialogStrings == null) { 644 mDialogStrings = new StringBuffer(); 645 } 646 mDialogStrings.append("ALERT: "); 647 mDialogStrings.append(message); 648 mDialogStrings.append('\n'); 649 result.confirm(); 650 return true; 651 } 652 653 @Override 654 public boolean onJsConfirm(WebView view, String url, String message, 655 JsResult result) { 656 if (mDialogStrings == null) { 657 mDialogStrings = new StringBuffer(); 658 } 659 mDialogStrings.append("CONFIRM: "); 660 mDialogStrings.append(message); 661 mDialogStrings.append('\n'); 662 result.confirm(); 663 return true; 664 } 665 666 @Override 667 public boolean onJsPrompt(WebView view, String url, String message, 668 String defaultValue, JsPromptResult result) { 669 if (mDialogStrings == null) { 670 mDialogStrings = new StringBuffer(); 671 } 672 mDialogStrings.append("PROMPT: "); 673 mDialogStrings.append(message); 674 mDialogStrings.append(", default text: "); 675 mDialogStrings.append(defaultValue); 676 mDialogStrings.append('\n'); 677 result.confirm(); 678 return true; 679 } 680 681 @Override 682 public boolean onJsTimeout() { 683 Log.v(LOGTAG, "JavaScript timeout"); 684 return false; 685 } 686 687 @Override 688 public void onExceededDatabaseQuota(String url_str, 689 String databaseIdentifier, long currentQuota, 690 long estimatedSize, long totalUsedQuota, 691 WebStorage.QuotaUpdater callback) { 692 if (mDumpDatabaseCallbacks) { 693 if (mDatabaseCallbackStrings == null) { 694 mDatabaseCallbackStrings = new StringBuffer(); 695 } 696 697 String protocol = ""; 698 String host = ""; 699 int port = 0; 700 701 try { 702 URL url = new URL(url_str); 703 protocol = url.getProtocol(); 704 host = url.getHost(); 705 if (url.getPort() > -1) { 706 port = url.getPort(); 707 } 708 } catch (MalformedURLException e) {} 709 710 String databaseCallbackString = 711 "UI DELEGATE DATABASE CALLBACK: " + 712 "exceededDatabaseQuotaForSecurityOrigin:{" + protocol + 713 ", " + host + ", " + port + "} database:" + 714 databaseIdentifier + "\n"; 715 Log.v(LOGTAG, "LOG: "+databaseCallbackString); 716 mDatabaseCallbackStrings.append(databaseCallbackString); 717 } 718 // Give 5MB more quota. 719 callback.updateQuota(currentQuota + 1024 * 1024 * 5); 720 } 721 722 /** 723 * Instructs the client to show a prompt to ask the user to set the 724 * Geolocation permission state for the specified origin. 725 */ 726 @Override 727 public void onGeolocationPermissionsShowPrompt(String origin, 728 GeolocationPermissions.Callback callback) { 729 if (mIsGeolocationPermissionSet) { 730 callback.invoke(origin, mGeolocationPermission, false); 731 return; 732 } 733 if (mPendingGeolocationPermissionCallbacks == null) { 734 mPendingGeolocationPermissionCallbacks = 735 new HashMap<GeolocationPermissions.Callback, String>(); 736 } 737 mPendingGeolocationPermissionCallbacks.put(callback, origin); 738 } 739 740 @Override 741 public boolean onConsoleMessage(ConsoleMessage consoleMessage) { 742 String msg = "CONSOLE MESSAGE: line " + consoleMessage.lineNumber() + ": " 743 + consoleMessage.message() + "\n"; 744 if (mConsoleMessages == null) { 745 mConsoleMessages = new StringBuffer(); 746 } 747 mConsoleMessages.append(msg); 748 Log.v(LOGTAG, "LOG: " + msg); 749 // the rationale here is that if there's an error of either type, and the test was 750 // waiting for "notifyDone" signal to finish, then there's no point in waiting 751 // anymore because the JS execution is already terminated at this point and a 752 // "notifyDone" will never come out so it's just wasting time till timeout kicks in 753 if ((msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:")) 754 && mWaitUntilDone && mStopOnRefError) { 755 Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError."); 756 mHandler.postDelayed(new Runnable() { 757 public void run() { 758 notifyDone(); 759 } 760 }, 500); 761 } 762 return true; 763 } 764 765 @Override 766 public boolean onCreateWindow(WebView view, boolean dialog, 767 boolean userGesture, Message resultMsg) { 768 if (!mCanOpenWindows) { 769 // We can't open windows, so just send null back. 770 WebView.WebViewTransport transport = 771 (WebView.WebViewTransport) resultMsg.obj; 772 transport.setWebView(null); 773 resultMsg.sendToTarget(); 774 return true; 775 } 776 777 // We never display the new window, just create the view and 778 // allow it's content to execute and be recorded by the test 779 // runner. 780 781 HashMap<String, Object> jsIfaces = new HashMap<String, Object>(); 782 jsIfaces.put("layoutTestController", mCallbackProxy); 783 jsIfaces.put("eventSender", mCallbackProxy); 784 WebView newWindowView = new NewWindowWebView(TestShellActivity.this, jsIfaces); 785 setupWebViewForLayoutTests(newWindowView, mCallbackProxy); 786 WebView.WebViewTransport transport = 787 (WebView.WebViewTransport) resultMsg.obj; 788 transport.setWebView(newWindowView); 789 resultMsg.sendToTarget(); 790 return true; 791 } 792 793 @Override 794 public void onCloseWindow(WebView view) { 795 view.destroy(); 796 } 797 }; 798 799 private static class NewWindowWebView extends WebView { 800 public NewWindowWebView(Context context, Map<String, Object> jsIfaces) { 801 super(context, null, 0, jsIfaces, false); 802 } 803 } 804 805 private void resetTestStatus() { 806 mWaitUntilDone = false; 807 mDumpDataType = mDefaultDumpDataType; 808 mDumpTopFrameAsText = false; 809 mDumpChildFramesAsText = false; 810 mTimedOut = false; 811 mDumpTitleChanges = false; 812 mRequestedWebKitData = false; 813 mDumpDatabaseCallbacks = false; 814 mCanOpenWindows = false; 815 mEventSender.resetMouse(); 816 mEventSender.clearTouchPoints(); 817 mEventSender.clearTouchMetaState(); 818 mPageFinished = false; 819 mDumpWebKitData = false; 820 setDefaultWebSettings(mWebView); 821 mIsGeolocationPermissionSet = false; 822 mPendingGeolocationPermissionCallbacks = null; 823 CookieManager.getInstance().removeAllCookie(); 824 } 825 826 private boolean canMoveToNextTest() { 827 return (mDumpWebKitData && mPageFinished && !mWaitUntilDone) || mTimedOut; 828 } 829 830 private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) { 831 if (webview == null) { 832 return; 833 } 834 835 setDefaultWebSettings(webview); 836 837 webview.setWebChromeClient(mChromeClient); 838 webview.setWebViewClient(mViewClient); 839 // Setting a touch interval of -1 effectively disables the optimisation in WebView 840 // that stops repeated touch events flooding WebCore. The Event Sender only sends a 841 // single event rather than a stream of events (like what would generally happen in 842 // a real use of touch events in a WebView) and so if the WebView drops the event, 843 // the test will fail as the test expects one callback for every touch it synthesizes. 844 WebViewClassic.fromWebView(webview).setTouchInterval(-1); 845 } 846 847 public void setDefaultWebSettings(WebView webview) { 848 WebSettingsClassic settings = WebViewClassic.fromWebView(webview).getSettings(); 849 settings.setAppCacheEnabled(true); 850 settings.setAppCachePath(getApplicationContext().getCacheDir().getPath()); 851 settings.setAppCacheMaxSize(Long.MAX_VALUE); 852 settings.setJavaScriptEnabled(true); 853 settings.setJavaScriptCanOpenWindowsAutomatically(true); 854 settings.setSupportMultipleWindows(true); 855 settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); 856 settings.setDatabaseEnabled(true); 857 settings.setDatabasePath(getDir("databases",0).getAbsolutePath()); 858 settings.setDomStorageEnabled(true); 859 settings.setWorkersEnabled(false); 860 settings.setXSSAuditorEnabled(false); 861 settings.setPageCacheCapacity(0); 862 settings.setProperty("use_minimal_memory", "false"); 863 } 864 865 private WebViewClassic mWebViewClassic; 866 private WebView mWebView; 867 private WebViewEventSender mEventSender; 868 private AsyncHandler mHandler; 869 private TestShellCallback mCallback; 870 871 private CallbackProxy mCallbackProxy; 872 873 private String mTestUrl; 874 private String mResultFile; 875 private int mTimeoutInMillis; 876 private String mUiAutoTestPath; 877 private BufferedReader mTestListReader; 878 private int mTotalTestCount; 879 private int mCurrentTestNumber; 880 private boolean mStopOnRefError; 881 882 // States 883 private boolean mTimedOut; 884 private boolean mRequestedWebKitData; 885 private boolean mFinishedRunning; 886 887 // Layout test controller variables. 888 private DumpDataType mDumpDataType; 889 private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR; 890 private boolean mDumpTopFrameAsText; 891 private boolean mDumpChildFramesAsText; 892 private boolean mWaitUntilDone; 893 private boolean mDumpTitleChanges; 894 private StringBuffer mTitleChanges; 895 private StringBuffer mDialogStrings; 896 private boolean mKeepWebHistory; 897 private Vector mWebHistory; 898 private boolean mDumpDatabaseCallbacks; 899 private StringBuffer mDatabaseCallbackStrings; 900 private StringBuffer mConsoleMessages; 901 private boolean mCanOpenWindows; 902 903 private boolean mPageFinished = false; 904 private boolean mDumpWebKitData = false; 905 906 static final String TIMEOUT_STR = "**Test timeout"; 907 static final long DUMP_TIMEOUT_MS = 100000; // 100s timeout for dumping webview content 908 909 static final int MSG_TIMEOUT = 0; 910 static final int MSG_WEBKIT_DATA = 1; 911 static final int MSG_DUMP_TIMEOUT = 2; 912 913 static final String LOGTAG="TestShell"; 914 915 static final String TEST_URL = "TestUrl"; 916 static final String RESULT_FILE = "ResultFile"; 917 static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis"; 918 static final String UI_AUTO_TEST = "UiAutoTest"; 919 static final String GET_DRAW_TIME = "GetDrawTime"; 920 static final String SAVE_IMAGE = "SaveImage"; 921 static final String TOTAL_TEST_COUNT = "TestCount"; 922 static final String CURRENT_TEST_NUMBER = "TestNumber"; 923 static final String STOP_ON_REF_ERROR = "StopOnReferenceError"; 924 925 static final int DRAW_RUNS = 5; 926 static final String DRAW_TIME_LOG = Environment.getExternalStorageDirectory() + 927 "/android/page_draw_time.txt"; 928 929 private boolean mIsGeolocationPermissionSet; 930 private boolean mGeolocationPermission; 931 private Map mPendingGeolocationPermissionCallbacks; 932} 933