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