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.setUseMockDeviceOrientation(); 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 @Override 261 public void onClick(DialogInterface dialog, int which) { 262 TestShellActivity.this.finish(); 263 } 264 }) 265 .setNegativeButton("No", new OnClickListener(){ 266 @Override 267 public void onClick(DialogInterface dialog, int which) { 268 dialog.cancel(); 269 } 270 }); 271 builder.create().show(); 272 return; 273 } 274 Intent intent = new Intent(Intent.ACTION_VIEW); 275 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 276 intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url)); 277 intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, ++mCurrentTestNumber); 278 intent.putExtra(TIMEOUT_IN_MILLIS, 10000); 279 executeIntent(intent); 280 } 281 282 @Override 283 protected void onStop() { 284 super.onStop(); 285 mWebView.stopLoading(); 286 } 287 288 @Override 289 protected void onDestroy() { 290 super.onDestroy(); 291 mWebView.destroy(); 292 mWebView = null; 293 mWebViewClassic = null; 294 } 295 296 @Override 297 public void onLowMemory() { 298 super.onLowMemory(); 299 Log.e(LOGTAG, "Low memory, clearing caches"); 300 mWebView.freeMemory(); 301 } 302 303 // Dump the page 304 public void dump(boolean timeout, String webkitData) { 305 mDumpWebKitData = true; 306 if (mResultFile == null || mResultFile.length() == 0) { 307 finished(); 308 return; 309 } 310 311 if (mCallback != null) { 312 mCallback.dumpResult(webkitData); 313 } 314 315 try { 316 File parentDir = new File(mResultFile).getParentFile(); 317 if (!parentDir.exists()) { 318 parentDir.mkdirs(); 319 } 320 321 FileOutputStream os = new FileOutputStream(mResultFile); 322 if (timeout) { 323 Log.w("Layout test: Timeout", mResultFile); 324 os.write(TIMEOUT_STR.getBytes()); 325 os.write('\n'); 326 } 327 if (mDumpTitleChanges) 328 os.write(mTitleChanges.toString().getBytes()); 329 if (mDialogStrings != null) 330 os.write(mDialogStrings.toString().getBytes()); 331 mDialogStrings = null; 332 if (mDatabaseCallbackStrings != null) 333 os.write(mDatabaseCallbackStrings.toString().getBytes()); 334 mDatabaseCallbackStrings = null; 335 if (mConsoleMessages != null) 336 os.write(mConsoleMessages.toString().getBytes()); 337 mConsoleMessages = null; 338 if (webkitData != null) 339 os.write(webkitData.getBytes()); 340 os.flush(); 341 os.close(); 342 } catch (IOException ex) { 343 Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage()); 344 } 345 346 finished(); 347 } 348 349 public void setCallback(TestShellCallback callback) { 350 mCallback = callback; 351 } 352 353 public boolean finished() { 354 if (canMoveToNextTest()) { 355 mHandler.removeMessages(MSG_TIMEOUT); 356 if (mUiAutoTestPath != null) { 357 //don't really finish here 358 moveToNextTest(); 359 } else { 360 if (mCallback != null) { 361 mCallback.finished(); 362 } 363 } 364 return true; 365 } 366 return false; 367 } 368 369 public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) { 370 mDefaultDumpDataType = defaultDumpDataType; 371 } 372 373 // ....................................... 374 // LayoutTestController Functions 375 @Override 376 public void dumpAsText(boolean enablePixelTests) { 377 // Added after webkit update to r63859. See trac.webkit.org/changeset/63730. 378 if (enablePixelTests) { 379 Log.v(LOGTAG, "dumpAsText(enablePixelTests == true) not implemented on Android!"); 380 } 381 382 mDumpDataType = DumpDataType.DUMP_AS_TEXT; 383 mDumpTopFrameAsText = true; 384 if (mWebView != null) { 385 String url = mWebView.getUrl(); 386 Log.v(LOGTAG, "dumpAsText called: "+url); 387 } 388 } 389 390 @Override 391 public void dumpChildFramesAsText() { 392 mDumpDataType = DumpDataType.DUMP_AS_TEXT; 393 mDumpChildFramesAsText = true; 394 if (mWebView != null) { 395 String url = mWebView.getUrl(); 396 Log.v(LOGTAG, "dumpChildFramesAsText called: "+url); 397 } 398 } 399 400 @Override 401 public void waitUntilDone() { 402 mWaitUntilDone = true; 403 String url = mWebView.getUrl(); 404 Log.v(LOGTAG, "waitUntilDone called: " + url); 405 } 406 407 @Override 408 public void notifyDone() { 409 String url = mWebView.getUrl(); 410 Log.v(LOGTAG, "notifyDone called: " + url); 411 if (mWaitUntilDone) { 412 mWaitUntilDone = false; 413 if (!mRequestedWebKitData && !mTimedOut && !finished()) { 414 requestWebKitData(); 415 } 416 } 417 } 418 419 @Override 420 public void display() { 421 mWebView.invalidate(); 422 } 423 424 @Override 425 public void clearBackForwardList() { 426 mWebView.clearHistory(); 427 428 } 429 430 @Override 431 public void dumpBackForwardList() { 432 //printf("\n============== Back Forward List ==============\n"); 433 // mWebHistory 434 //printf("===============================================\n"); 435 436 } 437 438 @Override 439 public void dumpChildFrameScrollPositions() { 440 // TODO Auto-generated method stub 441 442 } 443 444 @Override 445 public void dumpEditingCallbacks() { 446 // TODO Auto-generated method stub 447 448 } 449 450 @Override 451 public void dumpSelectionRect() { 452 // TODO Auto-generated method stub 453 454 } 455 456 @Override 457 public void dumpTitleChanges() { 458 if (!mDumpTitleChanges) { 459 mTitleChanges = new StringBuffer(); 460 } 461 mDumpTitleChanges = true; 462 } 463 464 @Override 465 public void keepWebHistory() { 466 if (!mKeepWebHistory) { 467 mWebHistory = new Vector(); 468 } 469 mKeepWebHistory = true; 470 } 471 472 @Override 473 public void queueBackNavigation(int howfar) { 474 // TODO Auto-generated method stub 475 476 } 477 478 @Override 479 public void queueForwardNavigation(int howfar) { 480 // TODO Auto-generated method stub 481 482 } 483 484 @Override 485 public void queueLoad(String Url, String frameTarget) { 486 // TODO Auto-generated method stub 487 488 } 489 490 @Override 491 public void queueReload() { 492 mWebView.reload(); 493 } 494 495 @Override 496 public void queueScript(String scriptToRunInCurrentContext) { 497 mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); 498 } 499 500 @Override 501 public void repaintSweepHorizontally() { 502 // TODO Auto-generated method stub 503 504 } 505 506 @Override 507 public void setAcceptsEditing(boolean b) { 508 // TODO Auto-generated method stub 509 510 } 511 512 @Override 513 public void setMainFrameIsFirstResponder(boolean b) { 514 // TODO Auto-generated method stub 515 516 } 517 518 @Override 519 public void setWindowIsKey(boolean b) { 520 // This is meant to show/hide the window. The best I can find 521 // is setEnabled() 522 mWebView.setEnabled(b); 523 } 524 525 @Override 526 public void testRepaint() { 527 mWebView.invalidate(); 528 } 529 530 @Override 531 public void dumpDatabaseCallbacks() { 532 Log.v(LOGTAG, "dumpDatabaseCallbacks called."); 533 mDumpDatabaseCallbacks = true; 534 } 535 536 @Override 537 public void setCanOpenWindows() { 538 Log.v(LOGTAG, "setCanOpenWindows called."); 539 mCanOpenWindows = true; 540 } 541 542 @Override 543 public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) { 544 WebViewClassic.fromWebView(mWebView).setMockGeolocationPosition(latitude, longitude, 545 accuracy); 546 } 547 548 @Override 549 public void setMockGeolocationError(int code, String message) { 550 WebViewClassic.fromWebView(mWebView).setMockGeolocationError(code, message); 551 } 552 553 @Override 554 public void setGeolocationPermission(boolean allow) { 555 Log.v(LOGTAG, "setGeolocationPermission() allow=" + allow); 556 WebViewClassic.fromWebView(mWebView).setMockGeolocationPermission(allow); 557 } 558 559 @Override 560 public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, 561 boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { 562 WebViewClassic.fromWebView(mWebView).setMockDeviceOrientation(canProvideAlpha, alpha, 563 canProvideBeta, beta, canProvideGamma, gamma); 564 } 565 566 @Override 567 public void overridePreference(String key, boolean value) { 568 // TODO: We should look up the correct WebView for the frame which 569 // called the layoutTestController method. Currently, we just use the 570 // WebView for the main frame. EventSender suffers from the same 571 // problem. 572 if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) { 573 mWebViewClassic.getSettings().setAppCacheEnabled(value); 574 } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) { 575 // Cache the maximum possible number of pages. 576 mWebViewClassic.getSettings().setPageCacheCapacity(Integer.MAX_VALUE); 577 } else { 578 Log.w(LOGTAG, "LayoutTestController.overridePreference(): " + 579 "Unsupported preference '" + key + "'"); 580 } 581 } 582 583 @Override 584 public void setXSSAuditorEnabled (boolean flag) { 585 mWebViewClassic.getSettings().setXSSAuditorEnabled(flag); 586 } 587 588 private final WebViewClient mViewClient = new WebViewClient(){ 589 @Override 590 public void onPageFinished(WebView view, String url) { 591 Log.v(LOGTAG, "onPageFinished, url=" + url); 592 mPageFinished = true; 593 594 // Calling finished() will check if we've met all the conditions for completing 595 // this test and move to the next one if we are ready. Otherwise we ask WebCore to 596 // dump the page. 597 if (finished()) { 598 return; 599 } 600 601 if (!mWaitUntilDone && !mRequestedWebKitData && !mTimedOut) { 602 requestWebKitData(); 603 } else { 604 if (mWaitUntilDone) { 605 Log.v(LOGTAG, "page finished loading but waiting for notifyDone to be called: " + url); 606 } 607 608 if (mRequestedWebKitData) { 609 Log.v(LOGTAG, "page finished loading but webkit data has already been requested: " + url); 610 } 611 612 if (mTimedOut) { 613 Log.v(LOGTAG, "page finished loading but already timed out: " + url); 614 } 615 } 616 617 super.onPageFinished(view, url); 618 } 619 620 @Override 621 public void onPageStarted(WebView view, String url, Bitmap favicon) { 622 Log.v(LOGTAG, "onPageStarted, url=" + url); 623 mPageFinished = false; 624 super.onPageStarted(view, url, favicon); 625 } 626 627 @Override 628 public void onReceivedError(WebView view, int errorCode, String description, 629 String failingUrl) { 630 Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode 631 + ", desc=" + description + ", url=" + failingUrl); 632 super.onReceivedError(view, errorCode, description, failingUrl); 633 } 634 635 @Override 636 public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, 637 String host, String realm) { 638 if (handler.useHttpAuthUsernamePassword() && view != null) { 639 String[] credentials = view.getHttpAuthUsernamePassword(host, realm); 640 if (credentials != null && credentials.length == 2) { 641 handler.proceed(credentials[0], credentials[1]); 642 return; 643 } 644 } 645 handler.cancel(); 646 } 647 648 @Override 649 public void onReceivedSslError(WebView view, SslErrorHandler handler, 650 SslError error) { 651 handler.proceed(); 652 } 653 }; 654 655 656 private final WebChromeClient mChromeClient = new WebChromeClient() { 657 @Override 658 public void onReceivedTitle(WebView view, String title) { 659 setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount + ": "+ title); 660 if (mDumpTitleChanges) { 661 mTitleChanges.append("TITLE CHANGED: "); 662 mTitleChanges.append(title); 663 mTitleChanges.append("\n"); 664 } 665 } 666 667 @Override 668 public boolean onJsAlert(WebView view, String url, String message, 669 JsResult result) { 670 if (mDialogStrings == null) { 671 mDialogStrings = new StringBuffer(); 672 } 673 mDialogStrings.append("ALERT: "); 674 mDialogStrings.append(message); 675 mDialogStrings.append('\n'); 676 result.confirm(); 677 return true; 678 } 679 680 @Override 681 public boolean onJsConfirm(WebView view, String url, String message, 682 JsResult result) { 683 if (mDialogStrings == null) { 684 mDialogStrings = new StringBuffer(); 685 } 686 mDialogStrings.append("CONFIRM: "); 687 mDialogStrings.append(message); 688 mDialogStrings.append('\n'); 689 result.confirm(); 690 return true; 691 } 692 693 @Override 694 public boolean onJsPrompt(WebView view, String url, String message, 695 String defaultValue, JsPromptResult result) { 696 if (mDialogStrings == null) { 697 mDialogStrings = new StringBuffer(); 698 } 699 mDialogStrings.append("PROMPT: "); 700 mDialogStrings.append(message); 701 mDialogStrings.append(", default text: "); 702 mDialogStrings.append(defaultValue); 703 mDialogStrings.append('\n'); 704 result.confirm(); 705 return true; 706 } 707 708 @Override 709 public boolean onJsTimeout() { 710 Log.v(LOGTAG, "JavaScript timeout"); 711 return false; 712 } 713 714 @Override 715 public void onExceededDatabaseQuota(String url_str, 716 String databaseIdentifier, long currentQuota, 717 long estimatedSize, long totalUsedQuota, 718 WebStorage.QuotaUpdater callback) { 719 if (mDumpDatabaseCallbacks) { 720 if (mDatabaseCallbackStrings == null) { 721 mDatabaseCallbackStrings = new StringBuffer(); 722 } 723 724 String protocol = ""; 725 String host = ""; 726 int port = 0; 727 728 try { 729 URL url = new URL(url_str); 730 protocol = url.getProtocol(); 731 host = url.getHost(); 732 if (url.getPort() > -1) { 733 port = url.getPort(); 734 } 735 } catch (MalformedURLException e) {} 736 737 String databaseCallbackString = 738 "UI DELEGATE DATABASE CALLBACK: " + 739 "exceededDatabaseQuotaForSecurityOrigin:{" + protocol + 740 ", " + host + ", " + port + "} database:" + 741 databaseIdentifier + "\n"; 742 Log.v(LOGTAG, "LOG: "+databaseCallbackString); 743 mDatabaseCallbackStrings.append(databaseCallbackString); 744 } 745 // Give 5MB more quota. 746 callback.updateQuota(currentQuota + 1024 * 1024 * 5); 747 } 748 749 @Override 750 public void onGeolocationPermissionsShowPrompt(String origin, 751 GeolocationPermissions.Callback callback) { 752 throw new RuntimeException( 753 "The WebCore mock used by DRT should bypass the usual permissions flow."); 754 } 755 756 @Override 757 public boolean onConsoleMessage(ConsoleMessage consoleMessage) { 758 String msg = "CONSOLE MESSAGE: line " + consoleMessage.lineNumber() + ": " 759 + consoleMessage.message() + "\n"; 760 if (mConsoleMessages == null) { 761 mConsoleMessages = new StringBuffer(); 762 } 763 mConsoleMessages.append(msg); 764 Log.v(LOGTAG, "LOG: " + msg); 765 // the rationale here is that if there's an error of either type, and the test was 766 // waiting for "notifyDone" signal to finish, then there's no point in waiting 767 // anymore because the JS execution is already terminated at this point and a 768 // "notifyDone" will never come out so it's just wasting time till timeout kicks in 769 if ((msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:")) 770 && mWaitUntilDone && mStopOnRefError) { 771 Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError."); 772 mHandler.postDelayed(new Runnable() { 773 @Override 774 public void run() { 775 notifyDone(); 776 } 777 }, 500); 778 } 779 return true; 780 } 781 782 @Override 783 public boolean onCreateWindow(WebView view, boolean dialog, 784 boolean userGesture, Message resultMsg) { 785 if (!mCanOpenWindows) { 786 // We can't open windows, so just send null back. 787 WebView.WebViewTransport transport = 788 (WebView.WebViewTransport) resultMsg.obj; 789 transport.setWebView(null); 790 resultMsg.sendToTarget(); 791 return true; 792 } 793 794 // We never display the new window, just create the view and 795 // allow it's content to execute and be recorded by the test 796 // runner. 797 798 HashMap<String, Object> jsIfaces = new HashMap<String, Object>(); 799 jsIfaces.put("layoutTestController", mCallbackProxy); 800 jsIfaces.put("eventSender", mCallbackProxy); 801 WebView newWindowView = new NewWindowWebView(TestShellActivity.this, jsIfaces); 802 setupWebViewForLayoutTests(newWindowView, mCallbackProxy); 803 WebView.WebViewTransport transport = 804 (WebView.WebViewTransport) resultMsg.obj; 805 transport.setWebView(newWindowView); 806 resultMsg.sendToTarget(); 807 return true; 808 } 809 810 @Override 811 public void onCloseWindow(WebView view) { 812 view.destroy(); 813 } 814 }; 815 816 private static class NewWindowWebView extends WebView { 817 public NewWindowWebView(Context context, Map<String, Object> jsIfaces) { 818 super(context, null, 0, jsIfaces, false); 819 } 820 } 821 822 private void resetTestStatus() { 823 mWaitUntilDone = false; 824 mDumpDataType = mDefaultDumpDataType; 825 mDumpTopFrameAsText = false; 826 mDumpChildFramesAsText = false; 827 mTimedOut = false; 828 mDumpTitleChanges = false; 829 mRequestedWebKitData = false; 830 mDumpDatabaseCallbacks = false; 831 mCanOpenWindows = false; 832 mEventSender.resetMouse(); 833 mEventSender.clearTouchPoints(); 834 mEventSender.clearTouchMetaState(); 835 mPageFinished = false; 836 mDumpWebKitData = false; 837 setDefaultWebSettings(mWebView); 838 CookieManager.getInstance().removeAllCookie(); 839 mWebViewClassic.setUseMockGeolocation(); 840 } 841 842 private boolean canMoveToNextTest() { 843 return (mDumpWebKitData && mPageFinished && !mWaitUntilDone) || mTimedOut; 844 } 845 846 private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) { 847 if (webview == null) { 848 return; 849 } 850 851 setDefaultWebSettings(webview); 852 853 webview.setWebChromeClient(mChromeClient); 854 webview.setWebViewClient(mViewClient); 855 // Setting a touch interval of -1 effectively disables the optimisation in WebView 856 // that stops repeated touch events flooding WebCore. The Event Sender only sends a 857 // single event rather than a stream of events (like what would generally happen in 858 // a real use of touch events in a WebView) and so if the WebView drops the event, 859 // the test will fail as the test expects one callback for every touch it synthesizes. 860 WebViewClassic.fromWebView(webview).setTouchInterval(-1); 861 } 862 863 public void setDefaultWebSettings(WebView webview) { 864 WebSettingsClassic settings = WebViewClassic.fromWebView(webview).getSettings(); 865 settings.setAppCacheEnabled(true); 866 settings.setAppCachePath(getApplicationContext().getCacheDir().getPath()); 867 settings.setAppCacheMaxSize(Long.MAX_VALUE); 868 settings.setJavaScriptEnabled(true); 869 settings.setJavaScriptCanOpenWindowsAutomatically(true); 870 settings.setSupportMultipleWindows(true); 871 settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); 872 settings.setDatabaseEnabled(true); 873 settings.setDatabasePath(getDir("databases",0).getAbsolutePath()); 874 settings.setDomStorageEnabled(true); 875 settings.setWorkersEnabled(false); 876 settings.setXSSAuditorEnabled(false); 877 settings.setPageCacheCapacity(0); 878 settings.setProperty("use_minimal_memory", "false"); 879 settings.setAllowUniversalAccessFromFileURLs(true); 880 settings.setAllowFileAccessFromFileURLs(true); 881 } 882 883 private WebViewClassic mWebViewClassic; 884 private WebView mWebView; 885 private WebViewEventSender mEventSender; 886 private AsyncHandler mHandler; 887 private TestShellCallback mCallback; 888 889 private CallbackProxy mCallbackProxy; 890 891 private String mTestUrl; 892 private String mResultFile; 893 private int mTimeoutInMillis; 894 private String mUiAutoTestPath; 895 private BufferedReader mTestListReader; 896 private int mTotalTestCount; 897 private int mCurrentTestNumber; 898 private boolean mStopOnRefError; 899 900 // States 901 private boolean mTimedOut; 902 private boolean mRequestedWebKitData; 903 private boolean mFinishedRunning; 904 905 // Layout test controller variables. 906 private DumpDataType mDumpDataType; 907 private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR; 908 private boolean mDumpTopFrameAsText; 909 private boolean mDumpChildFramesAsText; 910 private boolean mWaitUntilDone; 911 private boolean mDumpTitleChanges; 912 private StringBuffer mTitleChanges; 913 private StringBuffer mDialogStrings; 914 private boolean mKeepWebHistory; 915 private Vector mWebHistory; 916 private boolean mDumpDatabaseCallbacks; 917 private StringBuffer mDatabaseCallbackStrings; 918 private StringBuffer mConsoleMessages; 919 private boolean mCanOpenWindows; 920 921 private boolean mPageFinished = false; 922 private boolean mDumpWebKitData = false; 923 924 static final String TIMEOUT_STR = "**Test timeout"; 925 static final long DUMP_TIMEOUT_MS = 100000; // 100s timeout for dumping webview content 926 927 static final int MSG_TIMEOUT = 0; 928 static final int MSG_WEBKIT_DATA = 1; 929 static final int MSG_DUMP_TIMEOUT = 2; 930 931 static final String LOGTAG="TestShell"; 932 933 static final String TEST_URL = "TestUrl"; 934 static final String RESULT_FILE = "ResultFile"; 935 static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis"; 936 static final String UI_AUTO_TEST = "UiAutoTest"; 937 static final String GET_DRAW_TIME = "GetDrawTime"; 938 static final String SAVE_IMAGE = "SaveImage"; 939 static final String TOTAL_TEST_COUNT = "TestCount"; 940 static final String CURRENT_TEST_NUMBER = "TestNumber"; 941 static final String STOP_ON_REF_ERROR = "StopOnReferenceError"; 942 943 static final int DRAW_RUNS = 5; 944 static final String DRAW_TIME_LOG = Environment.getExternalStorageDirectory() + 945 "/android/page_draw_time.txt"; 946} 947