| /* |
| * 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.Intent; |
| import android.content.DialogInterface.OnClickListener; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.graphics.Bitmap.CompressFormat; |
| import android.graphics.Bitmap.Config; |
| 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.CookieManager; |
| 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.WebStorage; |
| import android.webkit.WebView; |
| 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; |
| mWebView.documentAsText(callback); |
| break; |
| case EXT_REPR: |
| mWebView.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); |
| 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. |
| mWebView.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. |
| mWebView.useMockDeviceOrientation(); |
| } |
| |
| @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); |
| mGetDrawtime = intent.getBooleanExtra(GET_DRAW_TIME, false); |
| mSaveImagePath = intent.getStringExtra(SAVE_IMAGE); |
| 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(){ |
| public void onClick(DialogInterface dialog, int which) { |
| TestShellActivity.this.finish(); |
| } |
| }) |
| .setNegativeButton("No", new OnClickListener(){ |
| 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; |
| } |
| |
| @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; |
| } |
| |
| 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 |
| 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); |
| } |
| } |
| |
| public void dumpChildFramesAsText() { |
| mDumpDataType = DumpDataType.DUMP_AS_TEXT; |
| mDumpChildFramesAsText = true; |
| if (mWebView != null) { |
| String url = mWebView.getUrl(); |
| Log.v(LOGTAG, "dumpChildFramesAsText called: "+url); |
| } |
| } |
| |
| public void waitUntilDone() { |
| mWaitUntilDone = true; |
| String url = mWebView.getUrl(); |
| Log.v(LOGTAG, "waitUntilDone called: " + url); |
| } |
| |
| public void notifyDone() { |
| String url = mWebView.getUrl(); |
| Log.v(LOGTAG, "notifyDone called: " + url); |
| if (mWaitUntilDone) { |
| mWaitUntilDone = false; |
| if (!mRequestedWebKitData && !mTimedOut && !finished()) { |
| requestWebKitData(); |
| } |
| } |
| } |
| |
| public void display() { |
| mWebView.invalidate(); |
| } |
| |
| public void clearBackForwardList() { |
| mWebView.clearHistory(); |
| |
| } |
| |
| public void dumpBackForwardList() { |
| //printf("\n============== Back Forward List ==============\n"); |
| // mWebHistory |
| //printf("===============================================\n"); |
| |
| } |
| |
| public void dumpChildFrameScrollPositions() { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void dumpEditingCallbacks() { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void dumpSelectionRect() { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void dumpTitleChanges() { |
| if (!mDumpTitleChanges) { |
| mTitleChanges = new StringBuffer(); |
| } |
| mDumpTitleChanges = true; |
| } |
| |
| public void keepWebHistory() { |
| if (!mKeepWebHistory) { |
| mWebHistory = new Vector(); |
| } |
| mKeepWebHistory = true; |
| } |
| |
| public void queueBackNavigation(int howfar) { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void queueForwardNavigation(int howfar) { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void queueLoad(String Url, String frameTarget) { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void queueReload() { |
| mWebView.reload(); |
| } |
| |
| public void queueScript(String scriptToRunInCurrentContext) { |
| mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); |
| } |
| |
| public void repaintSweepHorizontally() { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void setAcceptsEditing(boolean b) { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void setMainFrameIsFirstResponder(boolean b) { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| public void setWindowIsKey(boolean b) { |
| // This is meant to show/hide the window. The best I can find |
| // is setEnabled() |
| mWebView.setEnabled(b); |
| } |
| |
| public void testRepaint() { |
| mWebView.invalidate(); |
| } |
| |
| public void dumpDatabaseCallbacks() { |
| Log.v(LOGTAG, "dumpDatabaseCallbacks called."); |
| mDumpDatabaseCallbacks = true; |
| } |
| |
| public void setCanOpenWindows() { |
| Log.v(LOGTAG, "setCanOpenWindows called."); |
| mCanOpenWindows = true; |
| } |
| |
| /** |
| * Sets the Geolocation permission state to be used for all future requests. |
| */ |
| public void setGeolocationPermission(boolean allow) { |
| mIsGeolocationPermissionSet = true; |
| mGeolocationPermission = allow; |
| |
| if (mPendingGeolocationPermissionCallbacks != null) { |
| Iterator iter = mPendingGeolocationPermissionCallbacks.keySet().iterator(); |
| while (iter.hasNext()) { |
| GeolocationPermissions.Callback callback = |
| (GeolocationPermissions.Callback) iter.next(); |
| String origin = (String) mPendingGeolocationPermissionCallbacks.get(callback); |
| callback.invoke(origin, mGeolocationPermission, false); |
| } |
| mPendingGeolocationPermissionCallbacks = null; |
| } |
| } |
| |
| public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, |
| boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { |
| mWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta, |
| canProvideGamma, gamma); |
| } |
| |
| 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)) { |
| mWebView.getSettings().setAppCacheEnabled(value); |
| } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) { |
| // Cache the maximum possible number of pages. |
| mWebView.getSettings().setPageCacheCapacity(Integer.MAX_VALUE); |
| } else { |
| Log.w(LOGTAG, "LayoutTestController.overridePreference(): " + |
| "Unsupported preference '" + key + "'"); |
| } |
| } |
| |
| public void setXSSAuditorEnabled (boolean flag) { |
| mWebView.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; |
| // get page draw time |
| if (FsUtils.isTestPageUrl(url)) { |
| if (mGetDrawtime) { |
| long[] times = new long[DRAW_RUNS]; |
| times = getDrawWebViewTime(mWebView, DRAW_RUNS); |
| FsUtils.writeDrawTime(DRAW_TIME_LOG, url, times); |
| } |
| if (mSaveImagePath != null) { |
| String name = FsUtils.getLastSegmentInPath(url); |
| drawPageToFile(mSaveImagePath + "/" + name + ".png", mWebView); |
| } |
| } |
| |
| // 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); |
| } |
| |
| /** |
| * Instructs the client to show a prompt to ask the user to set the |
| * Geolocation permission state for the specified origin. |
| */ |
| @Override |
| public void onGeolocationPermissionsShowPrompt(String origin, |
| GeolocationPermissions.Callback callback) { |
| if (mIsGeolocationPermissionSet) { |
| callback.invoke(origin, mGeolocationPermission, false); |
| return; |
| } |
| if (mPendingGeolocationPermissionCallbacks == null) { |
| mPendingGeolocationPermissionCallbacks = |
| new HashMap<GeolocationPermissions.Callback, String>(); |
| } |
| mPendingGeolocationPermissionCallbacks.put(callback, origin); |
| } |
| |
| @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() { |
| 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; |
| mGetDrawtime = false; |
| mSaveImagePath = null; |
| setDefaultWebSettings(mWebView); |
| mIsGeolocationPermissionSet = false; |
| mPendingGeolocationPermissionCallbacks = null; |
| CookieManager.getInstance().removeAllCookie(); |
| } |
| |
| private long[] getDrawWebViewTime(WebView view, int count) { |
| if (count == 0) |
| return null; |
| long[] ret = new long[count]; |
| long start; |
| Canvas canvas = new Canvas(); |
| Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Config.ARGB_8888); |
| canvas.setBitmap(bitmap); |
| for (int i = 0; i < count; i++) { |
| start = System.currentTimeMillis(); |
| view.draw(canvas); |
| ret[i] = System.currentTimeMillis() - start; |
| } |
| return ret; |
| } |
| |
| private void drawPageToFile(String fileName, WebView view) { |
| Canvas canvas = new Canvas(); |
| Bitmap bitmap = Bitmap.createBitmap(view.getContentWidth(), view.getContentHeight(), |
| Config.ARGB_8888); |
| canvas.setBitmap(bitmap); |
| view.drawPage(canvas); |
| try { |
| FileOutputStream fos = new FileOutputStream(fileName); |
| if(!bitmap.compress(CompressFormat.PNG, 90, fos)) { |
| Log.w(LOGTAG, "Failed to compress and save image."); |
| } |
| } catch (IOException ioe) { |
| Log.e(LOGTAG, "", ioe); |
| } |
| bitmap.recycle(); |
| } |
| |
| 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. |
| webview.setTouchInterval(-1); |
| } |
| |
| public void setDefaultWebSettings(WebView webview) { |
| WebSettings settings = 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); |
| // this enables cpu upload path (as opposed to gpu upload path) |
| // and it's only meant to be a temporary workaround! |
| settings.setProperty("enable_cpu_upload_path", "true"); |
| } |
| |
| 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 String mSaveImagePath; |
| private BufferedReader mTestListReader; |
| private boolean mGetDrawtime; |
| 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"; |
| |
| private boolean mIsGeolocationPermissionSet; |
| private boolean mGeolocationPermission; |
| private Map mPendingGeolocationPermissionCallbacks; |
| } |