/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dumprendertree; import com.android.dumprendertree.forwarder.ForwardService; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.graphics.Bitmap; import android.net.http.SslError; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.ViewGroup; import android.view.Window; import android.webkit.ConsoleMessage; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; import android.webkit.HttpAuthHandler; import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.SslErrorHandler; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebSettingsClassic; import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.WebViewClassic; import android.webkit.WebViewClient; import android.widget.LinearLayout; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Vector; public class TestShellActivity extends Activity implements LayoutTestController { static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP} // String constants for use with layoutTestController.overridePreferences private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED = "WebKitOfflineWebApplicationCacheEnabled"; private final String WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY = "WebKitUsesPageCachePreferenceKey"; public class AsyncHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == MSG_TIMEOUT) { mTimedOut = true; mWebView.stopLoading(); if (mCallback != null) mCallback.timedOut(mWebView.getUrl()); if (!mRequestedWebKitData) { requestWebKitData(); } else { // if timed out and webkit data has been dumped before // finish directly finished(); } return; } else if (msg.what == MSG_WEBKIT_DATA) { Log.v(LOGTAG, "Received WebView dump data"); mHandler.removeMessages(MSG_DUMP_TIMEOUT); TestShellActivity.this.dump(mTimedOut, (String)msg.obj); return; } else if (msg.what == MSG_DUMP_TIMEOUT) { throw new RuntimeException("WebView dump timeout, is it pegged?"); } super.handleMessage(msg); } } public void requestWebKitData() { setDumpTimeout(DUMP_TIMEOUT_MS); Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA); if (mRequestedWebKitData) throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl()); mRequestedWebKitData = true; Log.v(LOGTAG, "message sent to WebView to dump text."); switch (mDumpDataType) { case DUMP_AS_TEXT: callback.arg1 = mDumpTopFrameAsText ? 1 : 0; callback.arg2 = mDumpChildFramesAsText ? 1 : 0; mWebViewClassic.documentAsText(callback); break; case EXT_REPR: mWebViewClassic.externalRepresentation(callback); break; default: finished(); break; } } private void setDumpTimeout(long timeout) { Log.v(LOGTAG, "setting dump timeout at " + timeout); Message msg = mHandler.obtainMessage(MSG_DUMP_TIMEOUT); mHandler.sendMessageDelayed(msg, timeout); } public void clearCache() { mWebView.freeMemory(); } @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); requestWindowFeature(Window.FEATURE_PROGRESS); LinearLayout contentView = new LinearLayout(this); contentView.setOrientation(LinearLayout.VERTICAL); setContentView(contentView); CookieManager.setAcceptFileSchemeCookies(true); mWebView = new WebView(this); mWebViewClassic = WebViewClassic.fromWebView(mWebView); mEventSender = new WebViewEventSender(mWebView); mCallbackProxy = new CallbackProxy(mEventSender, this); mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController"); mWebView.addJavascriptInterface(mCallbackProxy, "eventSender"); setupWebViewForLayoutTests(mWebView, mCallbackProxy); contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0f)); mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); // Expose window.gc function to JavaScript. JSC build exposes // this function by default, but V8 requires the flag to turn it on. // WebView::setJsFlags is noop in JSC build. mWebViewClassic.setJsFlags("--expose_gc"); mHandler = new AsyncHandler(); Intent intent = getIntent(); if (intent != null) { executeIntent(intent); } // This is asynchronous, but it gets processed by WebCore before it starts loading pages. mWebViewClassic.setUseMockDeviceOrientation(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); executeIntent(intent); } private void executeIntent(Intent intent) { resetTestStatus(); if (!Intent.ACTION_VIEW.equals(intent.getAction())) { return; } mTotalTestCount = intent.getIntExtra(TOTAL_TEST_COUNT, mTotalTestCount); mCurrentTestNumber = intent.getIntExtra(CURRENT_TEST_NUMBER, mCurrentTestNumber); mTestUrl = intent.getStringExtra(TEST_URL); if (mTestUrl == null) { mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST); if(mUiAutoTestPath != null) { beginUiAutoTest(); } return; } mResultFile = intent.getStringExtra(RESULT_FILE); mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0); mStopOnRefError = intent.getBooleanExtra(STOP_ON_REF_ERROR, false); setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount); float ratio = (float)mCurrentTestNumber / mTotalTestCount; int progress = (int)(ratio * Window.PROGRESS_END); getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress); Log.v(LOGTAG, " Loading " + mTestUrl); if (mTestUrl.contains("/dumpAsText/")) { dumpAsText(false); } mWebView.loadUrl(mTestUrl); if (mTimeoutInMillis > 0) { // Create a timeout timer Message m = mHandler.obtainMessage(MSG_TIMEOUT); mHandler.sendMessageDelayed(m, mTimeoutInMillis); } } private void beginUiAutoTest() { try { mTestListReader = new BufferedReader( new FileReader(mUiAutoTestPath)); } catch (IOException ioe) { Log.e(LOGTAG, "Failed to open test list for read.", ioe); finishUiAutoTest(); return; } moveToNextTest(); } private void finishUiAutoTest() { try { if(mTestListReader != null) mTestListReader.close(); } catch (IOException ioe) { Log.w(LOGTAG, "Failed to close test list file.", ioe); } ForwardService.getForwardService().stopForwardService(); finished(); } private void moveToNextTest() { String url = null; try { url = mTestListReader.readLine(); } catch (IOException ioe) { Log.e(LOGTAG, "Failed to read next test.", ioe); finishUiAutoTest(); return; } if (url == null) { mUiAutoTestPath = null; finishUiAutoTest(); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("All tests finished. Exit?") .setCancelable(false) .setPositiveButton("Yes", new OnClickListener(){ @Override public void onClick(DialogInterface dialog, int which) { TestShellActivity.this.finish(); } }) .setNegativeButton("No", new OnClickListener(){ @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); builder.create().show(); return; } Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url)); intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, ++mCurrentTestNumber); intent.putExtra(TIMEOUT_IN_MILLIS, 10000); executeIntent(intent); } @Override protected void onStop() { super.onStop(); mWebView.stopLoading(); } @Override protected void onDestroy() { super.onDestroy(); mWebView.destroy(); mWebView = null; mWebViewClassic = null; } @Override public void onLowMemory() { super.onLowMemory(); Log.e(LOGTAG, "Low memory, clearing caches"); mWebView.freeMemory(); } // Dump the page public void dump(boolean timeout, String webkitData) { mDumpWebKitData = true; if (mResultFile == null || mResultFile.length() == 0) { finished(); return; } if (mCallback != null) { mCallback.dumpResult(webkitData); } try { File parentDir = new File(mResultFile).getParentFile(); if (!parentDir.exists()) { parentDir.mkdirs(); } FileOutputStream os = new FileOutputStream(mResultFile); if (timeout) { Log.w("Layout test: Timeout", mResultFile); os.write(TIMEOUT_STR.getBytes()); os.write('\n'); } if (mDumpTitleChanges) os.write(mTitleChanges.toString().getBytes()); if (mDialogStrings != null) os.write(mDialogStrings.toString().getBytes()); mDialogStrings = null; if (mDatabaseCallbackStrings != null) os.write(mDatabaseCallbackStrings.toString().getBytes()); mDatabaseCallbackStrings = null; if (mConsoleMessages != null) os.write(mConsoleMessages.toString().getBytes()); mConsoleMessages = null; if (webkitData != null) os.write(webkitData.getBytes()); os.flush(); os.close(); } catch (IOException ex) { Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage()); } finished(); } public void setCallback(TestShellCallback callback) { mCallback = callback; } public boolean finished() { if (canMoveToNextTest()) { mHandler.removeMessages(MSG_TIMEOUT); if (mUiAutoTestPath != null) { //don't really finish here moveToNextTest(); } else { if (mCallback != null) { mCallback.finished(); } } return true; } return false; } public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) { mDefaultDumpDataType = defaultDumpDataType; } // ....................................... // LayoutTestController Functions @Override public void dumpAsText(boolean enablePixelTests) { // Added after webkit update to r63859. See trac.webkit.org/changeset/63730. if (enablePixelTests) { Log.v(LOGTAG, "dumpAsText(enablePixelTests == true) not implemented on Android!"); } mDumpDataType = DumpDataType.DUMP_AS_TEXT; mDumpTopFrameAsText = true; if (mWebView != null) { String url = mWebView.getUrl(); Log.v(LOGTAG, "dumpAsText called: "+url); } } @Override public void dumpChildFramesAsText() { mDumpDataType = DumpDataType.DUMP_AS_TEXT; mDumpChildFramesAsText = true; if (mWebView != null) { String url = mWebView.getUrl(); Log.v(LOGTAG, "dumpChildFramesAsText called: "+url); } } @Override public void waitUntilDone() { mWaitUntilDone = true; String url = mWebView.getUrl(); Log.v(LOGTAG, "waitUntilDone called: " + url); } @Override public void notifyDone() { String url = mWebView.getUrl(); Log.v(LOGTAG, "notifyDone called: " + url); if (mWaitUntilDone) { mWaitUntilDone = false; if (!mRequestedWebKitData && !mTimedOut && !finished()) { requestWebKitData(); } } } @Override public void display() { mWebView.invalidate(); } @Override public void clearBackForwardList() { mWebView.clearHistory(); } @Override public void dumpBackForwardList() { //printf("\n============== Back Forward List ==============\n"); // mWebHistory //printf("===============================================\n"); } @Override public void dumpChildFrameScrollPositions() { // TODO Auto-generated method stub } @Override public void dumpEditingCallbacks() { // TODO Auto-generated method stub } @Override public void dumpSelectionRect() { // TODO Auto-generated method stub } @Override public void dumpTitleChanges() { if (!mDumpTitleChanges) { mTitleChanges = new StringBuffer(); } mDumpTitleChanges = true; } @Override public void keepWebHistory() { if (!mKeepWebHistory) { mWebHistory = new Vector(); } mKeepWebHistory = true; } @Override public void queueBackNavigation(int howfar) { // TODO Auto-generated method stub } @Override public void queueForwardNavigation(int howfar) { // TODO Auto-generated method stub } @Override public void queueLoad(String Url, String frameTarget) { // TODO Auto-generated method stub } @Override public void queueReload() { mWebView.reload(); } @Override public void queueScript(String scriptToRunInCurrentContext) { mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); } @Override public void repaintSweepHorizontally() { // TODO Auto-generated method stub } @Override public void setAcceptsEditing(boolean b) { // TODO Auto-generated method stub } @Override public void setMainFrameIsFirstResponder(boolean b) { // TODO Auto-generated method stub } @Override public void setWindowIsKey(boolean b) { // This is meant to show/hide the window. The best I can find // is setEnabled() mWebView.setEnabled(b); } @Override public void testRepaint() { mWebView.invalidate(); } @Override public void dumpDatabaseCallbacks() { Log.v(LOGTAG, "dumpDatabaseCallbacks called."); mDumpDatabaseCallbacks = true; } @Override public void setCanOpenWindows() { Log.v(LOGTAG, "setCanOpenWindows called."); mCanOpenWindows = true; } @Override public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) { WebViewClassic.fromWebView(mWebView).setMockGeolocationPosition(latitude, longitude, accuracy); } @Override public void setMockGeolocationError(int code, String message) { WebViewClassic.fromWebView(mWebView).setMockGeolocationError(code, message); } @Override public void setGeolocationPermission(boolean allow) { Log.v(LOGTAG, "setGeolocationPermission() allow=" + allow); WebViewClassic.fromWebView(mWebView).setMockGeolocationPermission(allow); } @Override public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { WebViewClassic.fromWebView(mWebView).setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma); } @Override public void overridePreference(String key, boolean value) { // TODO: We should look up the correct WebView for the frame which // called the layoutTestController method. Currently, we just use the // WebView for the main frame. EventSender suffers from the same // problem. if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) { mWebViewClassic.getSettings().setAppCacheEnabled(value); } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) { // Cache the maximum possible number of pages. mWebViewClassic.getSettings().setPageCacheCapacity(Integer.MAX_VALUE); } else { Log.w(LOGTAG, "LayoutTestController.overridePreference(): " + "Unsupported preference '" + key + "'"); } } @Override public void setXSSAuditorEnabled (boolean flag) { mWebViewClassic.getSettings().setXSSAuditorEnabled(flag); } private final WebViewClient mViewClient = new WebViewClient(){ @Override public void onPageFinished(WebView view, String url) { Log.v(LOGTAG, "onPageFinished, url=" + url); mPageFinished = true; // Calling finished() will check if we've met all the conditions for completing // this test and move to the next one if we are ready. Otherwise we ask WebCore to // dump the page. if (finished()) { return; } if (!mWaitUntilDone && !mRequestedWebKitData && !mTimedOut) { requestWebKitData(); } else { if (mWaitUntilDone) { Log.v(LOGTAG, "page finished loading but waiting for notifyDone to be called: " + url); } if (mRequestedWebKitData) { Log.v(LOGTAG, "page finished loading but webkit data has already been requested: " + url); } if (mTimedOut) { Log.v(LOGTAG, "page finished loading but already timed out: " + url); } } super.onPageFinished(view, url); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { Log.v(LOGTAG, "onPageStarted, url=" + url); mPageFinished = false; super.onPageStarted(view, url, favicon); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode + ", desc=" + description + ", url=" + failingUrl); super.onReceivedError(view, errorCode, description, failingUrl); } @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { if (handler.useHttpAuthUsernamePassword() && view != null) { String[] credentials = view.getHttpAuthUsernamePassword(host, realm); if (credentials != null && credentials.length == 2) { handler.proceed(credentials[0], credentials[1]); return; } } handler.cancel(); } @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); } }; private final WebChromeClient mChromeClient = new WebChromeClient() { @Override public void onReceivedTitle(WebView view, String title) { setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount + ": "+ title); if (mDumpTitleChanges) { mTitleChanges.append("TITLE CHANGED: "); mTitleChanges.append(title); mTitleChanges.append("\n"); } } @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { if (mDialogStrings == null) { mDialogStrings = new StringBuffer(); } mDialogStrings.append("ALERT: "); mDialogStrings.append(message); mDialogStrings.append('\n'); result.confirm(); return true; } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { if (mDialogStrings == null) { mDialogStrings = new StringBuffer(); } mDialogStrings.append("CONFIRM: "); mDialogStrings.append(message); mDialogStrings.append('\n'); result.confirm(); return true; } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { if (mDialogStrings == null) { mDialogStrings = new StringBuffer(); } mDialogStrings.append("PROMPT: "); mDialogStrings.append(message); mDialogStrings.append(", default text: "); mDialogStrings.append(defaultValue); mDialogStrings.append('\n'); result.confirm(); return true; } @Override public boolean onJsTimeout() { Log.v(LOGTAG, "JavaScript timeout"); return false; } @Override public void onExceededDatabaseQuota(String url_str, String databaseIdentifier, long currentQuota, long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater callback) { if (mDumpDatabaseCallbacks) { if (mDatabaseCallbackStrings == null) { mDatabaseCallbackStrings = new StringBuffer(); } String protocol = ""; String host = ""; int port = 0; try { URL url = new URL(url_str); protocol = url.getProtocol(); host = url.getHost(); if (url.getPort() > -1) { port = url.getPort(); } } catch (MalformedURLException e) {} String databaseCallbackString = "UI DELEGATE DATABASE CALLBACK: " + "exceededDatabaseQuotaForSecurityOrigin:{" + protocol + ", " + host + ", " + port + "} database:" + databaseIdentifier + "\n"; Log.v(LOGTAG, "LOG: "+databaseCallbackString); mDatabaseCallbackStrings.append(databaseCallbackString); } // Give 5MB more quota. callback.updateQuota(currentQuota + 1024 * 1024 * 5); } @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { throw new RuntimeException( "The WebCore mock used by DRT should bypass the usual permissions flow."); } @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { String msg = "CONSOLE MESSAGE: line " + consoleMessage.lineNumber() + ": " + consoleMessage.message() + "\n"; if (mConsoleMessages == null) { mConsoleMessages = new StringBuffer(); } mConsoleMessages.append(msg); Log.v(LOGTAG, "LOG: " + msg); // the rationale here is that if there's an error of either type, and the test was // waiting for "notifyDone" signal to finish, then there's no point in waiting // anymore because the JS execution is already terminated at this point and a // "notifyDone" will never come out so it's just wasting time till timeout kicks in if ((msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:")) && mWaitUntilDone && mStopOnRefError) { Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError."); mHandler.postDelayed(new Runnable() { @Override public void run() { notifyDone(); } }, 500); } return true; } @Override public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture, Message resultMsg) { if (!mCanOpenWindows) { // We can't open windows, so just send null back. WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; transport.setWebView(null); resultMsg.sendToTarget(); return true; } // We never display the new window, just create the view and // allow it's content to execute and be recorded by the test // runner. HashMap<String, Object> jsIfaces = new HashMap<String, Object>(); jsIfaces.put("layoutTestController", mCallbackProxy); jsIfaces.put("eventSender", mCallbackProxy); WebView newWindowView = new NewWindowWebView(TestShellActivity.this, jsIfaces); setupWebViewForLayoutTests(newWindowView, mCallbackProxy); WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; transport.setWebView(newWindowView); resultMsg.sendToTarget(); return true; } @Override public void onCloseWindow(WebView view) { view.destroy(); } }; private static class NewWindowWebView extends WebView { public NewWindowWebView(Context context, Map<String, Object> jsIfaces) { super(context, null, 0, jsIfaces, false); } } private void resetTestStatus() { mWaitUntilDone = false; mDumpDataType = mDefaultDumpDataType; mDumpTopFrameAsText = false; mDumpChildFramesAsText = false; mTimedOut = false; mDumpTitleChanges = false; mRequestedWebKitData = false; mDumpDatabaseCallbacks = false; mCanOpenWindows = false; mEventSender.resetMouse(); mEventSender.clearTouchPoints(); mEventSender.clearTouchMetaState(); mPageFinished = false; mDumpWebKitData = false; setDefaultWebSettings(mWebView); CookieManager.getInstance().removeAllCookie(); mWebViewClassic.setUseMockGeolocation(); } private boolean canMoveToNextTest() { return (mDumpWebKitData && mPageFinished && !mWaitUntilDone) || mTimedOut; } private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) { if (webview == null) { return; } setDefaultWebSettings(webview); webview.setWebChromeClient(mChromeClient); webview.setWebViewClient(mViewClient); // Setting a touch interval of -1 effectively disables the optimisation in WebView // that stops repeated touch events flooding WebCore. The Event Sender only sends a // single event rather than a stream of events (like what would generally happen in // a real use of touch events in a WebView) and so if the WebView drops the event, // the test will fail as the test expects one callback for every touch it synthesizes. WebViewClassic.fromWebView(webview).setTouchInterval(-1); } public void setDefaultWebSettings(WebView webview) { WebSettingsClassic settings = WebViewClassic.fromWebView(webview).getSettings(); settings.setAppCacheEnabled(true); settings.setAppCachePath(getApplicationContext().getCacheDir().getPath()); settings.setAppCacheMaxSize(Long.MAX_VALUE); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setSupportMultipleWindows(true); settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); settings.setDatabaseEnabled(true); settings.setDatabasePath(getDir("databases",0).getAbsolutePath()); settings.setDomStorageEnabled(true); settings.setWorkersEnabled(false); settings.setXSSAuditorEnabled(false); settings.setPageCacheCapacity(0); settings.setProperty("use_minimal_memory", "false"); settings.setAllowUniversalAccessFromFileURLs(true); settings.setAllowFileAccessFromFileURLs(true); } private WebViewClassic mWebViewClassic; private WebView mWebView; private WebViewEventSender mEventSender; private AsyncHandler mHandler; private TestShellCallback mCallback; private CallbackProxy mCallbackProxy; private String mTestUrl; private String mResultFile; private int mTimeoutInMillis; private String mUiAutoTestPath; private BufferedReader mTestListReader; private int mTotalTestCount; private int mCurrentTestNumber; private boolean mStopOnRefError; // States private boolean mTimedOut; private boolean mRequestedWebKitData; private boolean mFinishedRunning; // Layout test controller variables. private DumpDataType mDumpDataType; private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR; private boolean mDumpTopFrameAsText; private boolean mDumpChildFramesAsText; private boolean mWaitUntilDone; private boolean mDumpTitleChanges; private StringBuffer mTitleChanges; private StringBuffer mDialogStrings; private boolean mKeepWebHistory; private Vector mWebHistory; private boolean mDumpDatabaseCallbacks; private StringBuffer mDatabaseCallbackStrings; private StringBuffer mConsoleMessages; private boolean mCanOpenWindows; private boolean mPageFinished = false; private boolean mDumpWebKitData = false; static final String TIMEOUT_STR = "**Test timeout"; static final long DUMP_TIMEOUT_MS = 100000; // 100s timeout for dumping webview content static final int MSG_TIMEOUT = 0; static final int MSG_WEBKIT_DATA = 1; static final int MSG_DUMP_TIMEOUT = 2; static final String LOGTAG="TestShell"; static final String TEST_URL = "TestUrl"; static final String RESULT_FILE = "ResultFile"; static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis"; static final String UI_AUTO_TEST = "UiAutoTest"; static final String GET_DRAW_TIME = "GetDrawTime"; static final String SAVE_IMAGE = "SaveImage"; static final String TOTAL_TEST_COUNT = "TestCount"; static final String CURRENT_TEST_NUMBER = "TestNumber"; static final String STOP_ON_REF_ERROR = "StopOnReferenceError"; static final int DRAW_RUNS = 5; static final String DRAW_TIME_LOG = Environment.getExternalStorageDirectory() + "/android/page_draw_time.txt"; }