1. Remove mDeferMultiTouch in WebView, it was for testing only but we don't need it now since we always pass MultiTouch to WebKit. 2. Remove the use of mDeferMultiTouch in DRT tests. 3. Correct the index of getX(Y) for the second touch point in the debug string of MotionEvent. Change-Id: Ib63cfc5935af1a169ed26b2b138f74908492bc18
970 lines
34 KiB
Java
970 lines
34 KiB
Java
/*
|
|
* 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.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) {
|
|
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() {
|
|
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;
|
|
setDumpTimeout(DUMP_TIMEOUT_MS);
|
|
mWebView.documentAsText(callback);
|
|
break;
|
|
case EXT_REPR:
|
|
mWebView.externalRepresentation(callback);
|
|
setDumpTimeout(DUMP_TIMEOUT_MS);
|
|
break;
|
|
default:
|
|
finished();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void setDumpTimeout(long 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);
|
|
|
|
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);
|
|
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);
|
|
}
|
|
|
|
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 = 20000; //20s 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;
|
|
}
|