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