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