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