TestShellActivity.java revision e6f3e45dc4d8a2b482183c57c61dd9ea22505c85
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 android.app.Activity; 20import android.content.Intent; 21import android.graphics.Bitmap; 22import android.net.http.SslError; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.Message; 26import android.util.Log; 27import android.view.ViewGroup; 28import android.webkit.HttpAuthHandler; 29import android.webkit.JsPromptResult; 30import android.webkit.JsResult; 31import android.webkit.SslErrorHandler; 32import android.webkit.WebChromeClient; 33import android.webkit.WebSettings; 34import android.webkit.WebStorage; 35import android.webkit.WebView; 36import android.webkit.WebViewClient; 37import android.widget.LinearLayout; 38 39import java.io.File; 40import java.io.FileOutputStream; 41import java.io.IOException; 42import java.net.MalformedURLException; 43import java.net.URL; 44import java.util.Vector; 45 46public class TestShellActivity extends Activity implements LayoutTestController { 47 48 static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP} 49 50 public class AsyncHandler extends Handler { 51 @Override 52 public void handleMessage(Message msg) { 53 if (msg.what == MSG_TIMEOUT) { 54 mTimedOut = true; 55 mCallback.timedOut(mWebView.getUrl()); 56 requestWebKitData(); 57 return; 58 } else if (msg.what == MSG_WEBKIT_DATA) { 59 TestShellActivity.this.dump(mTimedOut, (String)msg.obj); 60 return; 61 } 62 63 super.handleMessage(msg); 64 } 65 } 66 67 public void requestWebKitData() { 68 Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA); 69 70 if (mRequestedWebKitData) 71 throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl()); 72 73 mRequestedWebKitData = true; 74 switch (mDumpDataType) { 75 case DUMP_AS_TEXT: 76 mWebView.documentAsText(callback); 77 break; 78 case EXT_REPR: 79 mWebView.externalRepresentation(callback); 80 break; 81 default: 82 finished(); 83 break; 84 } 85 } 86 87 @Override 88 protected void onCreate(Bundle icicle) { 89 super.onCreate(icicle); 90 91 LinearLayout contentView = new LinearLayout(this); 92 contentView.setOrientation(LinearLayout.VERTICAL); 93 setContentView(contentView); 94 95 mWebView = new WebView(this); 96 mEventSender = new WebViewEventSender(mWebView); 97 mCallbackProxy = new CallbackProxy(mEventSender, this); 98 99 setupWebViewForLayoutTests(mWebView, mCallbackProxy); 100 101 contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f)); 102 103 mHandler = new AsyncHandler(); 104 105 Intent intent = getIntent(); 106 if (intent != null) { 107 executeIntent(intent); 108 } 109 } 110 111 @Override 112 protected void onNewIntent(Intent intent) { 113 super.onNewIntent(intent); 114 executeIntent(intent); 115 } 116 117 private void executeIntent(Intent intent) { 118 resetTestStatus(); 119 if (!Intent.ACTION_VIEW.equals(intent.getAction())) { 120 return; 121 } 122 123 mTestUrl = intent.getStringExtra(TEST_URL); 124 if (mTestUrl == null) 125 return; 126 127 mResultFile = intent.getStringExtra(RESULT_FILE); 128 mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0); 129 130 Log.v(LOGTAG, " Loading " + mTestUrl); 131 mWebView.loadUrl(mTestUrl); 132 133 if (mTimeoutInMillis > 0) { 134 // Create a timeout timer 135 Message m = mHandler.obtainMessage(MSG_TIMEOUT); 136 mHandler.sendMessageDelayed(m, mTimeoutInMillis); 137 } 138 } 139 140 @Override 141 protected void onStop() { 142 super.onStop(); 143 mWebView.stopLoading(); 144 } 145 146 @Override 147 protected void onDestroy() { 148 super.onDestroy(); 149 mWebView.destroy(); 150 mWebView = null; 151 } 152 153 @Override 154 public void onLowMemory() { 155 super.onLowMemory(); 156 Log.e(LOGTAG, "Low memory, kill self"); 157 System.exit(1); 158 } 159 160 // Dump the page 161 public void dump(boolean timeout, String webkitData) { 162 if (mResultFile == null || mResultFile.length() == 0) { 163 finished(); 164 return; 165 } 166 167 try { 168 File parentDir = new File(mResultFile).getParentFile(); 169 if (!parentDir.exists()) { 170 parentDir.mkdirs(); 171 } 172 173 FileOutputStream os = new FileOutputStream(mResultFile); 174 if (timeout) { 175 Log.w("Layout test: Timeout", mResultFile); 176 os.write(TIMEOUT_STR.getBytes()); 177 os.write('\n'); 178 } 179 if (mDumpTitleChanges) 180 os.write(mTitleChanges.toString().getBytes()); 181 if (mDialogStrings != null) 182 os.write(mDialogStrings.toString().getBytes()); 183 mDialogStrings = null; 184 if (mDatabaseCallbackStrings != null) 185 os.write(mDatabaseCallbackStrings.toString().getBytes()); 186 mDatabaseCallbackStrings = null; 187 if (mConsoleMessages != null) 188 os.write(mConsoleMessages.toString().getBytes()); 189 mConsoleMessages = null; 190 if (webkitData != null) 191 os.write(webkitData.getBytes()); 192 os.flush(); 193 os.close(); 194 } catch (IOException ex) { 195 Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage()); 196 } 197 198 finished(); 199 } 200 201 public void setCallback(TestShellCallback callback) { 202 mCallback = callback; 203 } 204 205 public void finished() { 206 if (mCallback != null) { 207 mCallback.finished(); 208 } 209 } 210 211 public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) { 212 mDefaultDumpDataType = defaultDumpDataType; 213 } 214 215 // ....................................... 216 // LayoutTestController Functions 217 public void dumpAsText() { 218 mDumpDataType = DumpDataType.DUMP_AS_TEXT; 219 if (mWebView != null) { 220 String url = mWebView.getUrl(); 221 Log.v(LOGTAG, "dumpAsText called: "+url); 222 } 223 } 224 225 public void waitUntilDone() { 226 mWaitUntilDone = true; 227 String url = mWebView.getUrl(); 228 Log.v(LOGTAG, "waitUntilDone called: " + url); 229 } 230 231 public void notifyDone() { 232 String url = mWebView.getUrl(); 233 Log.v(LOGTAG, "notifyDone called: " + url); 234 if (mWaitUntilDone) { 235 mWaitUntilDone = false; 236 mChromeClient.onProgressChanged(mWebView, 100); 237 } 238 } 239 240 public void display() { 241 mWebView.invalidate(); 242 } 243 244 public void clearBackForwardList() { 245 mWebView.clearHistory(); 246 247 } 248 249 public void dumpBackForwardList() { 250 //printf("\n============== Back Forward List ==============\n"); 251 // mWebHistory 252 //printf("===============================================\n"); 253 254 } 255 256 public void dumpChildFrameScrollPositions() { 257 // TODO Auto-generated method stub 258 259 } 260 261 public void dumpEditingCallbacks() { 262 // TODO Auto-generated method stub 263 264 } 265 266 public void dumpSelectionRect() { 267 // TODO Auto-generated method stub 268 269 } 270 271 public void dumpTitleChanges() { 272 if (!mDumpTitleChanges) { 273 mTitleChanges = new StringBuffer(); 274 } 275 mDumpTitleChanges = true; 276 } 277 278 public void keepWebHistory() { 279 if (!mKeepWebHistory) { 280 mWebHistory = new Vector(); 281 } 282 mKeepWebHistory = true; 283 } 284 285 public void queueBackNavigation(int howfar) { 286 // TODO Auto-generated method stub 287 288 } 289 290 public void queueForwardNavigation(int howfar) { 291 // TODO Auto-generated method stub 292 293 } 294 295 public void queueLoad(String Url, String frameTarget) { 296 // TODO Auto-generated method stub 297 298 } 299 300 public void queueReload() { 301 mWebView.reload(); 302 } 303 304 public void queueScript(String scriptToRunInCurrentContext) { 305 mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); 306 } 307 308 public void repaintSweepHorizontally() { 309 // TODO Auto-generated method stub 310 311 } 312 313 public void setAcceptsEditing(boolean b) { 314 // TODO Auto-generated method stub 315 316 } 317 318 public void setMainFrameIsFirstResponder(boolean b) { 319 // TODO Auto-generated method stub 320 321 } 322 323 public void setWindowIsKey(boolean b) { 324 // This is meant to show/hide the window. The best I can find 325 // is setEnabled() 326 mWebView.setEnabled(b); 327 } 328 329 public void testRepaint() { 330 mWebView.invalidate(); 331 } 332 333 public void dumpDatabaseCallbacks() { 334 Log.v(LOGTAG, "dumpDatabaseCallbacks called."); 335 mDumpDatabaseCallbacks = true; 336 } 337 338 public void setCanOpenWindows() { 339 Log.v(LOGTAG, "setCanOpenWindows called."); 340 mCanOpenWindows = true; 341 } 342 343 private final WebViewClient mViewClient = new WebViewClient(){ 344 @Override 345 public void onPageFinished(WebView view, String url) { 346 Log.v(LOGTAG, "onPageFinished, url=" + url); 347 super.onPageFinished(view, url); 348 } 349 350 @Override 351 public void onPageStarted(WebView view, String url, Bitmap favicon) { 352 Log.v(LOGTAG, "onPageStarted, url=" + url); 353 super.onPageStarted(view, url, favicon); 354 } 355 356 @Override 357 public void onReceivedError(WebView view, int errorCode, String description, 358 String failingUrl) { 359 Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode 360 + ", desc=" + description + ", url=" + failingUrl); 361 super.onReceivedError(view, errorCode, description, failingUrl); 362 } 363 364 @Override 365 public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, 366 String host, String realm) { 367 handler.cancel(); 368 } 369 370 @Override 371 public void onReceivedSslError(WebView view, SslErrorHandler handler, 372 SslError error) { 373 handler.proceed(); 374 } 375 }; 376 377 378 private final WebChromeClient mChromeClient = new WebChromeClient() { 379 @Override 380 public void onProgressChanged(WebView view, int newProgress) { 381 if (newProgress == 100) { 382 if (!mTimedOut && !mWaitUntilDone && !mRequestedWebKitData) { 383 String url = mWebView.getUrl(); 384 Log.v(LOGTAG, "Finished: "+ url); 385 mHandler.removeMessages(MSG_TIMEOUT); 386 requestWebKitData(); 387 } else { 388 String url = mWebView.getUrl(); 389 if (mTimedOut) { 390 Log.v(LOGTAG, "Timed out before finishing: " + url); 391 } else if (mWaitUntilDone) { 392 Log.v(LOGTAG, "Waiting for notifyDone: " + url); 393 } else if (mRequestedWebKitData) { 394 Log.v(LOGTAG, "Requested webkit data ready: " + url); 395 } 396 } 397 } 398 } 399 400 @Override 401 public void onReceivedTitle(WebView view, String title) { 402 if (title.length() > 30) 403 title = "..."+title.substring(title.length()-30); 404 setTitle(title); 405 if (mDumpTitleChanges) { 406 mTitleChanges.append("TITLE CHANGED: "); 407 mTitleChanges.append(title); 408 mTitleChanges.append("\n"); 409 } 410 } 411 412 @Override 413 public boolean onJsAlert(WebView view, String url, String message, 414 JsResult result) { 415 if (mDialogStrings == null) { 416 mDialogStrings = new StringBuffer(); 417 } 418 mDialogStrings.append("ALERT: "); 419 mDialogStrings.append(message); 420 mDialogStrings.append('\n'); 421 result.confirm(); 422 return true; 423 } 424 425 @Override 426 public boolean onJsConfirm(WebView view, String url, String message, 427 JsResult result) { 428 if (mDialogStrings == null) { 429 mDialogStrings = new StringBuffer(); 430 } 431 mDialogStrings.append("CONFIRM: "); 432 mDialogStrings.append(message); 433 mDialogStrings.append('\n'); 434 result.confirm(); 435 return true; 436 } 437 438 @Override 439 public boolean onJsPrompt(WebView view, String url, String message, 440 String defaultValue, JsPromptResult result) { 441 if (mDialogStrings == null) { 442 mDialogStrings = new StringBuffer(); 443 } 444 mDialogStrings.append("PROMPT: "); 445 mDialogStrings.append(message); 446 mDialogStrings.append(", default text: "); 447 mDialogStrings.append(defaultValue); 448 mDialogStrings.append('\n'); 449 result.confirm(); 450 return true; 451 } 452 453 @Override 454 public void onExceededDatabaseQuota(String url_str, 455 String databaseIdentifier, long currentQuota, 456 WebStorage.QuotaUpdater callback) { 457 if (mDumpDatabaseCallbacks) { 458 if (mDatabaseCallbackStrings == null) { 459 mDatabaseCallbackStrings = new StringBuffer(); 460 } 461 462 String protocol = ""; 463 String host = ""; 464 int port = 0; 465 466 try { 467 URL url = new URL(url_str); 468 protocol = url.getProtocol(); 469 host = url.getHost(); 470 if (url.getPort() > -1) { 471 port = url.getPort(); 472 } 473 } catch (MalformedURLException e) {} 474 475 String databaseCallbackString = 476 "UI DELEGATE DATABASE CALLBACK: " + 477 "exceededDatabaseQuotaForSecurityOrigin:{" + protocol + 478 ", " + host + ", " + port + "} database:" + 479 databaseIdentifier + "\n"; 480 Log.v(LOGTAG, "LOG: "+databaseCallbackString); 481 mDatabaseCallbackStrings.append(databaseCallbackString); 482 } 483 // Give 5MB more quota. 484 callback.updateQuota(currentQuota + 1024 * 1024 * 5); 485 } 486 487 @Override 488 public void addMessageToConsole(String message, int lineNumber, 489 String sourceID) { 490 if (mConsoleMessages == null) { 491 mConsoleMessages = new StringBuffer(); 492 } 493 String consoleMessage = "CONSOLE MESSAGE: line " 494 + lineNumber +": "+ message +"\n"; 495 mConsoleMessages.append(consoleMessage); 496 Log.v(LOGTAG, "LOG: "+consoleMessage); 497 } 498 499 @Override 500 public boolean onCreateWindow(WebView view, boolean dialog, 501 boolean userGesture, Message resultMsg) { 502 if (!mCanOpenWindows) { 503 return false; 504 } 505 506 // We never display the new window, just create the view and 507 // allow it's content to execute and be recorded by the test 508 // runner. 509 510 WebView newWindowView = new WebView(TestShellActivity.this); 511 setupWebViewForLayoutTests(newWindowView, mCallbackProxy); 512 WebView.WebViewTransport transport = 513 (WebView.WebViewTransport) resultMsg.obj; 514 transport.setWebView(newWindowView); 515 resultMsg.sendToTarget(); 516 return true; 517 } 518 }; 519 520 private void resetTestStatus() { 521 mWaitUntilDone = false; 522 mDumpDataType = mDefaultDumpDataType; 523 mTimedOut = false; 524 mDumpTitleChanges = false; 525 mRequestedWebKitData = false; 526 mDumpDatabaseCallbacks = false; 527 mCanOpenWindows = false; 528 mEventSender.resetMouse(); 529 } 530 531 private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) { 532 if (webview == null) { 533 return; 534 } 535 536 WebSettings settings = webview.getSettings(); 537 settings.setJavaScriptEnabled(true); 538 settings.setJavaScriptCanOpenWindowsAutomatically(true); 539 settings.setSupportMultipleWindows(true); 540 settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); 541 settings.setDatabaseEnabled(true); 542 settings.setDatabasePath(getDir("databases",0).getAbsolutePath()); 543 settings.setDomStorageEnabled(true); 544 545 webview.addJavascriptInterface(callbackProxy, "layoutTestController"); 546 webview.addJavascriptInterface(callbackProxy, "eventSender"); 547 548 webview.setWebChromeClient(mChromeClient); 549 webview.setWebViewClient(mViewClient); 550 } 551 552 private WebView mWebView; 553 private WebViewEventSender mEventSender; 554 private AsyncHandler mHandler; 555 private TestShellCallback mCallback; 556 557 private CallbackProxy mCallbackProxy; 558 559 private String mTestUrl; 560 private String mResultFile; 561 private int mTimeoutInMillis; 562 563 // States 564 private boolean mTimedOut; 565 private boolean mRequestedWebKitData; 566 private boolean mFinishedRunning; 567 568 // Layout test controller variables. 569 private DumpDataType mDumpDataType; 570 private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR; 571 private boolean mWaitUntilDone; 572 private boolean mDumpTitleChanges; 573 private StringBuffer mTitleChanges; 574 private StringBuffer mDialogStrings; 575 private boolean mKeepWebHistory; 576 private Vector mWebHistory; 577 private boolean mDumpDatabaseCallbacks; 578 private StringBuffer mDatabaseCallbackStrings; 579 private StringBuffer mConsoleMessages; 580 private boolean mCanOpenWindows; 581 582 static final String TIMEOUT_STR = "**Test timeout"; 583 584 static final int MSG_TIMEOUT = 0; 585 static final int MSG_WEBKIT_DATA = 1; 586 587 static final String LOGTAG="TestShell"; 588 589 static final String TEST_URL = "TestUrl"; 590 static final String RESULT_FILE = "ResultFile"; 591 static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis"; 592} 593