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