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