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