Home | History | Annotate | Download | only in dumprendertree
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dumprendertree;
     18 
     19 import com.android.dumprendertree.forwarder.ForwardService;
     20 
     21 import android.app.Activity;
     22 import android.app.AlertDialog;
     23 import android.content.Context;
     24 import android.content.DialogInterface;
     25 import android.content.DialogInterface.OnClickListener;
     26 import android.content.Intent;
     27 import android.graphics.Bitmap;
     28 import android.net.http.SslError;
     29 import android.os.Bundle;
     30 import android.os.Environment;
     31 import android.os.Handler;
     32 import android.os.Message;
     33 import android.util.Log;
     34 import android.view.ViewGroup;
     35 import android.view.Window;
     36 import android.webkit.ConsoleMessage;
     37 import android.webkit.CookieManager;
     38 import android.webkit.GeolocationPermissions;
     39 import android.webkit.HttpAuthHandler;
     40 import android.webkit.JsPromptResult;
     41 import android.webkit.JsResult;
     42 import android.webkit.SslErrorHandler;
     43 import android.webkit.WebChromeClient;
     44 import android.webkit.WebSettings;
     45 import android.webkit.WebSettingsClassic;
     46 import android.webkit.WebStorage;
     47 import android.webkit.WebView;
     48 import android.webkit.WebViewClassic;
     49 import android.webkit.WebViewClient;
     50 import android.widget.LinearLayout;
     51 
     52 import java.io.BufferedReader;
     53 import java.io.File;
     54 import java.io.FileOutputStream;
     55 import java.io.FileReader;
     56 import java.io.IOException;
     57 import java.net.MalformedURLException;
     58 import java.net.URL;
     59 import java.util.HashMap;
     60 import java.util.Iterator;
     61 import java.util.Map;
     62 import java.util.Vector;
     63 
     64 public class TestShellActivity extends Activity implements LayoutTestController {
     65 
     66     static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
     67 
     68     // String constants for use with layoutTestController.overridePreferences
     69     private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED =
     70             "WebKitOfflineWebApplicationCacheEnabled";
     71     private final String WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY = "WebKitUsesPageCachePreferenceKey";
     72 
     73     public class AsyncHandler extends Handler {
     74         @Override
     75         public void handleMessage(Message msg) {
     76             if (msg.what == MSG_TIMEOUT) {
     77                 mTimedOut = true;
     78                 mWebView.stopLoading();
     79                 if (mCallback != null)
     80                     mCallback.timedOut(mWebView.getUrl());
     81                 if (!mRequestedWebKitData) {
     82                     requestWebKitData();
     83                 } else {
     84                     // if timed out and webkit data has been dumped before
     85                     // finish directly
     86                     finished();
     87                 }
     88                 return;
     89             } else if (msg.what == MSG_WEBKIT_DATA) {
     90                 Log.v(LOGTAG, "Received WebView dump data");
     91                 mHandler.removeMessages(MSG_DUMP_TIMEOUT);
     92                 TestShellActivity.this.dump(mTimedOut, (String)msg.obj);
     93                 return;
     94             } else if (msg.what == MSG_DUMP_TIMEOUT) {
     95                 throw new RuntimeException("WebView dump timeout, is it pegged?");
     96             }
     97             super.handleMessage(msg);
     98         }
     99     }
    100 
    101     public void requestWebKitData() {
    102         setDumpTimeout(DUMP_TIMEOUT_MS);
    103         Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA);
    104 
    105         if (mRequestedWebKitData)
    106             throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl());
    107 
    108         mRequestedWebKitData = true;
    109         Log.v(LOGTAG, "message sent to WebView to dump text.");
    110         switch (mDumpDataType) {
    111             case DUMP_AS_TEXT:
    112                 callback.arg1 = mDumpTopFrameAsText ? 1 : 0;
    113                 callback.arg2 = mDumpChildFramesAsText ? 1 : 0;
    114                 mWebViewClassic.documentAsText(callback);
    115                 break;
    116             case EXT_REPR:
    117                 mWebViewClassic.externalRepresentation(callback);
    118                 break;
    119             default:
    120                 finished();
    121                 break;
    122         }
    123     }
    124 
    125     private void setDumpTimeout(long timeout) {
    126         Log.v(LOGTAG, "setting dump timeout at " + timeout);
    127         Message msg = mHandler.obtainMessage(MSG_DUMP_TIMEOUT);
    128         mHandler.sendMessageDelayed(msg, timeout);
    129     }
    130 
    131     public void clearCache() {
    132       mWebView.freeMemory();
    133     }
    134 
    135     @Override
    136     protected void onCreate(Bundle icicle) {
    137         super.onCreate(icicle);
    138         requestWindowFeature(Window.FEATURE_PROGRESS);
    139 
    140         LinearLayout contentView = new LinearLayout(this);
    141         contentView.setOrientation(LinearLayout.VERTICAL);
    142         setContentView(contentView);
    143 
    144         CookieManager.setAcceptFileSchemeCookies(true);
    145         mWebView = new WebView(this);
    146         mWebViewClassic = WebViewClassic.fromWebView(mWebView);
    147         mEventSender = new WebViewEventSender(mWebView);
    148         mCallbackProxy = new CallbackProxy(mEventSender, this);
    149 
    150         mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
    151         mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
    152         setupWebViewForLayoutTests(mWebView, mCallbackProxy);
    153 
    154         contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0f));
    155 
    156         mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
    157 
    158         // Expose window.gc function to JavaScript. JSC build exposes
    159         // this function by default, but V8 requires the flag to turn it on.
    160         // WebView::setJsFlags is noop in JSC build.
    161         mWebViewClassic.setJsFlags("--expose_gc");
    162 
    163         mHandler = new AsyncHandler();
    164 
    165         Intent intent = getIntent();
    166         if (intent != null) {
    167             executeIntent(intent);
    168         }
    169 
    170         // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
    171         mWebViewClassic.setUseMockDeviceOrientation();
    172     }
    173 
    174     @Override
    175     protected void onNewIntent(Intent intent) {
    176         super.onNewIntent(intent);
    177         executeIntent(intent);
    178     }
    179 
    180     private void executeIntent(Intent intent) {
    181         resetTestStatus();
    182         if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
    183             return;
    184         }
    185 
    186         mTotalTestCount = intent.getIntExtra(TOTAL_TEST_COUNT, mTotalTestCount);
    187         mCurrentTestNumber = intent.getIntExtra(CURRENT_TEST_NUMBER, mCurrentTestNumber);
    188 
    189         mTestUrl = intent.getStringExtra(TEST_URL);
    190         if (mTestUrl == null) {
    191             mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
    192             if(mUiAutoTestPath != null) {
    193                 beginUiAutoTest();
    194             }
    195             return;
    196         }
    197 
    198         mResultFile = intent.getStringExtra(RESULT_FILE);
    199         mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
    200         mStopOnRefError = intent.getBooleanExtra(STOP_ON_REF_ERROR, false);
    201         setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount);
    202         float ratio = (float)mCurrentTestNumber / mTotalTestCount;
    203         int progress = (int)(ratio * Window.PROGRESS_END);
    204         getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress);
    205 
    206         Log.v(LOGTAG, "  Loading " + mTestUrl);
    207 
    208         if (mTestUrl.contains("/dumpAsText/")) {
    209             dumpAsText(false);
    210         }
    211 
    212         mWebView.loadUrl(mTestUrl);
    213 
    214         if (mTimeoutInMillis > 0) {
    215             // Create a timeout timer
    216             Message m = mHandler.obtainMessage(MSG_TIMEOUT);
    217             mHandler.sendMessageDelayed(m, mTimeoutInMillis);
    218         }
    219     }
    220 
    221     private void beginUiAutoTest() {
    222         try {
    223             mTestListReader = new BufferedReader(
    224                     new FileReader(mUiAutoTestPath));
    225         } catch (IOException ioe) {
    226             Log.e(LOGTAG, "Failed to open test list for read.", ioe);
    227             finishUiAutoTest();
    228             return;
    229         }
    230         moveToNextTest();
    231     }
    232 
    233     private void finishUiAutoTest() {
    234         try {
    235             if(mTestListReader != null)
    236                 mTestListReader.close();
    237         } catch (IOException ioe) {
    238             Log.w(LOGTAG, "Failed to close test list file.", ioe);
    239         }
    240         ForwardService.getForwardService().stopForwardService();
    241         finished();
    242     }
    243 
    244     private void moveToNextTest() {
    245         String url = null;
    246         try {
    247             url = mTestListReader.readLine();
    248         } catch (IOException ioe) {
    249             Log.e(LOGTAG, "Failed to read next test.", ioe);
    250             finishUiAutoTest();
    251             return;
    252         }
    253         if (url == null) {
    254             mUiAutoTestPath = null;
    255             finishUiAutoTest();
    256             AlertDialog.Builder builder = new AlertDialog.Builder(this);
    257             builder.setMessage("All tests finished. Exit?")
    258                    .setCancelable(false)
    259                    .setPositiveButton("Yes", new OnClickListener(){
    260                        @Override
    261                     public void onClick(DialogInterface dialog, int which) {
    262                            TestShellActivity.this.finish();
    263                        }
    264                    })
    265                    .setNegativeButton("No", new OnClickListener(){
    266                        @Override
    267                     public void onClick(DialogInterface dialog, int which) {
    268                            dialog.cancel();
    269                        }
    270                    });
    271             builder.create().show();
    272             return;
    273         }
    274         Intent intent = new Intent(Intent.ACTION_VIEW);
    275         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    276         intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url));
    277         intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, ++mCurrentTestNumber);
    278         intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
    279         executeIntent(intent);
    280     }
    281 
    282     @Override
    283     protected void onStop() {
    284         super.onStop();
    285         mWebView.stopLoading();
    286     }
    287 
    288     @Override
    289     protected void onDestroy() {
    290         super.onDestroy();
    291         mWebView.destroy();
    292         mWebView = null;
    293         mWebViewClassic = null;
    294     }
    295 
    296     @Override
    297     public void onLowMemory() {
    298         super.onLowMemory();
    299         Log.e(LOGTAG, "Low memory, clearing caches");
    300         mWebView.freeMemory();
    301     }
    302 
    303     // Dump the page
    304     public void dump(boolean timeout, String webkitData) {
    305         mDumpWebKitData = true;
    306         if (mResultFile == null || mResultFile.length() == 0) {
    307             finished();
    308             return;
    309         }
    310 
    311         if (mCallback != null) {
    312             mCallback.dumpResult(webkitData);
    313         }
    314 
    315         try {
    316             File parentDir = new File(mResultFile).getParentFile();
    317             if (!parentDir.exists()) {
    318                 parentDir.mkdirs();
    319             }
    320 
    321             FileOutputStream os = new FileOutputStream(mResultFile);
    322             if (timeout) {
    323                 Log.w("Layout test: Timeout", mResultFile);
    324                 os.write(TIMEOUT_STR.getBytes());
    325                 os.write('\n');
    326             }
    327             if (mDumpTitleChanges)
    328                 os.write(mTitleChanges.toString().getBytes());
    329             if (mDialogStrings != null)
    330                 os.write(mDialogStrings.toString().getBytes());
    331             mDialogStrings = null;
    332             if (mDatabaseCallbackStrings != null)
    333                 os.write(mDatabaseCallbackStrings.toString().getBytes());
    334             mDatabaseCallbackStrings = null;
    335             if (mConsoleMessages != null)
    336                 os.write(mConsoleMessages.toString().getBytes());
    337             mConsoleMessages = null;
    338             if (webkitData != null)
    339                 os.write(webkitData.getBytes());
    340             os.flush();
    341             os.close();
    342         } catch (IOException ex) {
    343             Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
    344         }
    345 
    346         finished();
    347     }
    348 
    349     public void setCallback(TestShellCallback callback) {
    350         mCallback = callback;
    351     }
    352 
    353     public boolean finished() {
    354         if (canMoveToNextTest()) {
    355             mHandler.removeMessages(MSG_TIMEOUT);
    356             if (mUiAutoTestPath != null) {
    357                 //don't really finish here
    358                 moveToNextTest();
    359             } else {
    360                 if (mCallback != null) {
    361                     mCallback.finished();
    362                 }
    363             }
    364             return true;
    365         }
    366         return false;
    367     }
    368 
    369     public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) {
    370         mDefaultDumpDataType = defaultDumpDataType;
    371     }
    372 
    373     // .......................................
    374     // LayoutTestController Functions
    375     @Override
    376     public void dumpAsText(boolean enablePixelTests) {
    377         // Added after webkit update to r63859. See trac.webkit.org/changeset/63730.
    378         if (enablePixelTests) {
    379             Log.v(LOGTAG, "dumpAsText(enablePixelTests == true) not implemented on Android!");
    380         }
    381 
    382         mDumpDataType = DumpDataType.DUMP_AS_TEXT;
    383         mDumpTopFrameAsText = true;
    384         if (mWebView != null) {
    385             String url = mWebView.getUrl();
    386             Log.v(LOGTAG, "dumpAsText called: "+url);
    387         }
    388     }
    389 
    390     @Override
    391     public void dumpChildFramesAsText() {
    392         mDumpDataType = DumpDataType.DUMP_AS_TEXT;
    393         mDumpChildFramesAsText = true;
    394         if (mWebView != null) {
    395             String url = mWebView.getUrl();
    396             Log.v(LOGTAG, "dumpChildFramesAsText called: "+url);
    397         }
    398     }
    399 
    400     @Override
    401     public void waitUntilDone() {
    402         mWaitUntilDone = true;
    403         String url = mWebView.getUrl();
    404         Log.v(LOGTAG, "waitUntilDone called: " + url);
    405     }
    406 
    407     @Override
    408     public void notifyDone() {
    409         String url = mWebView.getUrl();
    410         Log.v(LOGTAG, "notifyDone called: " + url);
    411         if (mWaitUntilDone) {
    412             mWaitUntilDone = false;
    413             if (!mRequestedWebKitData && !mTimedOut && !finished()) {
    414                 requestWebKitData();
    415             }
    416         }
    417     }
    418 
    419     @Override
    420     public void display() {
    421         mWebView.invalidate();
    422     }
    423 
    424     @Override
    425     public void clearBackForwardList() {
    426         mWebView.clearHistory();
    427 
    428     }
    429 
    430     @Override
    431     public void dumpBackForwardList() {
    432         //printf("\n============== Back Forward List ==============\n");
    433         // mWebHistory
    434         //printf("===============================================\n");
    435 
    436     }
    437 
    438     @Override
    439     public void dumpChildFrameScrollPositions() {
    440         // TODO Auto-generated method stub
    441 
    442     }
    443 
    444     @Override
    445     public void dumpEditingCallbacks() {
    446         // TODO Auto-generated method stub
    447 
    448     }
    449 
    450     @Override
    451     public void dumpSelectionRect() {
    452         // TODO Auto-generated method stub
    453 
    454     }
    455 
    456     @Override
    457     public void dumpTitleChanges() {
    458         if (!mDumpTitleChanges) {
    459             mTitleChanges = new StringBuffer();
    460         }
    461         mDumpTitleChanges = true;
    462     }
    463 
    464     @Override
    465     public void keepWebHistory() {
    466         if (!mKeepWebHistory) {
    467             mWebHistory = new Vector();
    468         }
    469         mKeepWebHistory = true;
    470     }
    471 
    472     @Override
    473     public void queueBackNavigation(int howfar) {
    474         // TODO Auto-generated method stub
    475 
    476     }
    477 
    478     @Override
    479     public void queueForwardNavigation(int howfar) {
    480         // TODO Auto-generated method stub
    481 
    482     }
    483 
    484     @Override
    485     public void queueLoad(String Url, String frameTarget) {
    486         // TODO Auto-generated method stub
    487 
    488     }
    489 
    490     @Override
    491     public void queueReload() {
    492         mWebView.reload();
    493     }
    494 
    495     @Override
    496     public void queueScript(String scriptToRunInCurrentContext) {
    497         mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
    498     }
    499 
    500     @Override
    501     public void repaintSweepHorizontally() {
    502         // TODO Auto-generated method stub
    503 
    504     }
    505 
    506     @Override
    507     public void setAcceptsEditing(boolean b) {
    508         // TODO Auto-generated method stub
    509 
    510     }
    511 
    512     @Override
    513     public void setMainFrameIsFirstResponder(boolean b) {
    514         // TODO Auto-generated method stub
    515 
    516     }
    517 
    518     @Override
    519     public void setWindowIsKey(boolean b) {
    520         // This is meant to show/hide the window. The best I can find
    521         // is setEnabled()
    522         mWebView.setEnabled(b);
    523     }
    524 
    525     @Override
    526     public void testRepaint() {
    527         mWebView.invalidate();
    528     }
    529 
    530     @Override
    531     public void dumpDatabaseCallbacks() {
    532         Log.v(LOGTAG, "dumpDatabaseCallbacks called.");
    533         mDumpDatabaseCallbacks = true;
    534     }
    535 
    536     @Override
    537     public void setCanOpenWindows() {
    538         Log.v(LOGTAG, "setCanOpenWindows called.");
    539         mCanOpenWindows = true;
    540     }
    541 
    542     /**
    543      * Sets the Geolocation permission state to be used for all future requests.
    544      */
    545     @Override
    546     public void setGeolocationPermission(boolean allow) {
    547         mIsGeolocationPermissionSet = true;
    548         mGeolocationPermission = allow;
    549 
    550         if (mPendingGeolocationPermissionCallbacks != null) {
    551             Iterator iter = mPendingGeolocationPermissionCallbacks.keySet().iterator();
    552             while (iter.hasNext()) {
    553                 GeolocationPermissions.Callback callback =
    554                         (GeolocationPermissions.Callback) iter.next();
    555                 String origin = (String) mPendingGeolocationPermissionCallbacks.get(callback);
    556                 callback.invoke(origin, mGeolocationPermission, false);
    557             }
    558             mPendingGeolocationPermissionCallbacks = null;
    559         }
    560     }
    561 
    562     @Override
    563     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
    564             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
    565         WebViewClassic.fromWebView(mWebView).setMockDeviceOrientation(canProvideAlpha, alpha,
    566                 canProvideBeta, beta, canProvideGamma, gamma);
    567     }
    568 
    569     @Override
    570     public void overridePreference(String key, boolean value) {
    571         // TODO: We should look up the correct WebView for the frame which
    572         // called the layoutTestController method. Currently, we just use the
    573         // WebView for the main frame. EventSender suffers from the same
    574         // problem.
    575         if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) {
    576             mWebViewClassic.getSettings().setAppCacheEnabled(value);
    577         } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) {
    578             // Cache the maximum possible number of pages.
    579             mWebViewClassic.getSettings().setPageCacheCapacity(Integer.MAX_VALUE);
    580         } else {
    581             Log.w(LOGTAG, "LayoutTestController.overridePreference(): " +
    582                   "Unsupported preference '" + key + "'");
    583         }
    584     }
    585 
    586     @Override
    587     public void setXSSAuditorEnabled (boolean flag) {
    588         mWebViewClassic.getSettings().setXSSAuditorEnabled(flag);
    589     }
    590 
    591     private final WebViewClient mViewClient = new WebViewClient(){
    592         @Override
    593         public void onPageFinished(WebView view, String url) {
    594             Log.v(LOGTAG, "onPageFinished, url=" + url);
    595             mPageFinished = true;
    596 
    597             // Calling finished() will check if we've met all the conditions for completing
    598             // this test and move to the next one if we are ready. Otherwise we ask WebCore to
    599             // dump the page.
    600             if (finished()) {
    601                 return;
    602             }
    603 
    604             if (!mWaitUntilDone && !mRequestedWebKitData && !mTimedOut) {
    605                 requestWebKitData();
    606             } else {
    607                 if (mWaitUntilDone) {
    608                     Log.v(LOGTAG, "page finished loading but waiting for notifyDone to be called: " + url);
    609                 }
    610 
    611                 if (mRequestedWebKitData) {
    612                     Log.v(LOGTAG, "page finished loading but webkit data has already been requested: " + url);
    613                 }
    614 
    615                 if (mTimedOut) {
    616                     Log.v(LOGTAG, "page finished loading but already timed out: " + url);
    617                 }
    618             }
    619 
    620             super.onPageFinished(view, url);
    621         }
    622 
    623         @Override
    624         public void onPageStarted(WebView view, String url, Bitmap favicon) {
    625             Log.v(LOGTAG, "onPageStarted, url=" + url);
    626             mPageFinished = false;
    627             super.onPageStarted(view, url, favicon);
    628         }
    629 
    630         @Override
    631         public void onReceivedError(WebView view, int errorCode, String description,
    632                 String failingUrl) {
    633             Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
    634                     + ", desc=" + description + ", url=" + failingUrl);
    635             super.onReceivedError(view, errorCode, description, failingUrl);
    636         }
    637 
    638         @Override
    639         public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
    640                 String host, String realm) {
    641             if (handler.useHttpAuthUsernamePassword() && view != null) {
    642                 String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
    643                 if (credentials != null && credentials.length == 2) {
    644                     handler.proceed(credentials[0], credentials[1]);
    645                     return;
    646                 }
    647             }
    648             handler.cancel();
    649         }
    650 
    651         @Override
    652         public void onReceivedSslError(WebView view, SslErrorHandler handler,
    653                 SslError error) {
    654             handler.proceed();
    655         }
    656     };
    657 
    658 
    659     private final WebChromeClient mChromeClient = new WebChromeClient() {
    660         @Override
    661         public void onReceivedTitle(WebView view, String title) {
    662             setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount + ": "+ title);
    663             if (mDumpTitleChanges) {
    664                 mTitleChanges.append("TITLE CHANGED: ");
    665                 mTitleChanges.append(title);
    666                 mTitleChanges.append("\n");
    667             }
    668         }
    669 
    670         @Override
    671         public boolean onJsAlert(WebView view, String url, String message,
    672                 JsResult result) {
    673             if (mDialogStrings == null) {
    674                 mDialogStrings = new StringBuffer();
    675             }
    676             mDialogStrings.append("ALERT: ");
    677             mDialogStrings.append(message);
    678             mDialogStrings.append('\n');
    679             result.confirm();
    680             return true;
    681         }
    682 
    683         @Override
    684         public boolean onJsConfirm(WebView view, String url, String message,
    685                 JsResult result) {
    686             if (mDialogStrings == null) {
    687                 mDialogStrings = new StringBuffer();
    688             }
    689             mDialogStrings.append("CONFIRM: ");
    690             mDialogStrings.append(message);
    691             mDialogStrings.append('\n');
    692             result.confirm();
    693             return true;
    694         }
    695 
    696         @Override
    697         public boolean onJsPrompt(WebView view, String url, String message,
    698                 String defaultValue, JsPromptResult result) {
    699             if (mDialogStrings == null) {
    700                 mDialogStrings = new StringBuffer();
    701             }
    702             mDialogStrings.append("PROMPT: ");
    703             mDialogStrings.append(message);
    704             mDialogStrings.append(", default text: ");
    705             mDialogStrings.append(defaultValue);
    706             mDialogStrings.append('\n');
    707             result.confirm();
    708             return true;
    709         }
    710 
    711         @Override
    712         public boolean onJsTimeout() {
    713             Log.v(LOGTAG, "JavaScript timeout");
    714             return false;
    715         }
    716 
    717         @Override
    718         public void onExceededDatabaseQuota(String url_str,
    719                 String databaseIdentifier, long currentQuota,
    720                 long estimatedSize, long totalUsedQuota,
    721                 WebStorage.QuotaUpdater callback) {
    722             if (mDumpDatabaseCallbacks) {
    723                 if (mDatabaseCallbackStrings == null) {
    724                     mDatabaseCallbackStrings = new StringBuffer();
    725                 }
    726 
    727                 String protocol = "";
    728                 String host = "";
    729                 int port = 0;
    730 
    731                 try {
    732                     URL url = new URL(url_str);
    733                     protocol = url.getProtocol();
    734                     host = url.getHost();
    735                     if (url.getPort() > -1) {
    736                         port = url.getPort();
    737                     }
    738                 } catch (MalformedURLException e) {}
    739 
    740                 String databaseCallbackString =
    741                         "UI DELEGATE DATABASE CALLBACK: " +
    742                         "exceededDatabaseQuotaForSecurityOrigin:{" + protocol +
    743                         ", " + host + ", " + port + "} database:" +
    744                         databaseIdentifier + "\n";
    745                 Log.v(LOGTAG, "LOG: "+databaseCallbackString);
    746                 mDatabaseCallbackStrings.append(databaseCallbackString);
    747             }
    748             // Give 5MB more quota.
    749             callback.updateQuota(currentQuota + 1024 * 1024 * 5);
    750         }
    751 
    752         /**
    753          * Instructs the client to show a prompt to ask the user to set the
    754          * Geolocation permission state for the specified origin.
    755          */
    756         @Override
    757         public void onGeolocationPermissionsShowPrompt(String origin,
    758                 GeolocationPermissions.Callback callback) {
    759             if (mIsGeolocationPermissionSet) {
    760                 callback.invoke(origin, mGeolocationPermission, false);
    761                 return;
    762             }
    763             if (mPendingGeolocationPermissionCallbacks == null) {
    764                 mPendingGeolocationPermissionCallbacks =
    765                         new HashMap<GeolocationPermissions.Callback, String>();
    766             }
    767             mPendingGeolocationPermissionCallbacks.put(callback, origin);
    768         }
    769 
    770         @Override
    771         public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
    772             String msg = "CONSOLE MESSAGE: line " + consoleMessage.lineNumber() + ": "
    773                     + consoleMessage.message() + "\n";
    774             if (mConsoleMessages == null) {
    775                 mConsoleMessages = new StringBuffer();
    776             }
    777             mConsoleMessages.append(msg);
    778             Log.v(LOGTAG, "LOG: " + msg);
    779             // the rationale here is that if there's an error of either type, and the test was
    780             // waiting for "notifyDone" signal to finish, then there's no point in waiting
    781             // anymore because the JS execution is already terminated at this point and a
    782             // "notifyDone" will never come out so it's just wasting time till timeout kicks in
    783             if ((msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:"))
    784                     && mWaitUntilDone && mStopOnRefError) {
    785                 Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError.");
    786                 mHandler.postDelayed(new Runnable() {
    787                     @Override
    788                     public void run() {
    789                         notifyDone();
    790                     }
    791                 }, 500);
    792             }
    793             return true;
    794         }
    795 
    796         @Override
    797         public boolean onCreateWindow(WebView view, boolean dialog,
    798                 boolean userGesture, Message resultMsg) {
    799             if (!mCanOpenWindows) {
    800                 // We can't open windows, so just send null back.
    801                 WebView.WebViewTransport transport =
    802                         (WebView.WebViewTransport) resultMsg.obj;
    803                 transport.setWebView(null);
    804                 resultMsg.sendToTarget();
    805                 return true;
    806             }
    807 
    808             // We never display the new window, just create the view and
    809             // allow it's content to execute and be recorded by the test
    810             // runner.
    811 
    812             HashMap<String, Object> jsIfaces = new HashMap<String, Object>();
    813             jsIfaces.put("layoutTestController", mCallbackProxy);
    814             jsIfaces.put("eventSender", mCallbackProxy);
    815             WebView newWindowView = new NewWindowWebView(TestShellActivity.this, jsIfaces);
    816             setupWebViewForLayoutTests(newWindowView, mCallbackProxy);
    817             WebView.WebViewTransport transport =
    818                     (WebView.WebViewTransport) resultMsg.obj;
    819             transport.setWebView(newWindowView);
    820             resultMsg.sendToTarget();
    821             return true;
    822         }
    823 
    824         @Override
    825         public void onCloseWindow(WebView view) {
    826             view.destroy();
    827         }
    828     };
    829 
    830     private static class NewWindowWebView extends WebView {
    831         public NewWindowWebView(Context context, Map<String, Object> jsIfaces) {
    832             super(context, null, 0, jsIfaces, false);
    833         }
    834     }
    835 
    836     private void resetTestStatus() {
    837         mWaitUntilDone = false;
    838         mDumpDataType = mDefaultDumpDataType;
    839         mDumpTopFrameAsText = false;
    840         mDumpChildFramesAsText = false;
    841         mTimedOut = false;
    842         mDumpTitleChanges = false;
    843         mRequestedWebKitData = false;
    844         mDumpDatabaseCallbacks = false;
    845         mCanOpenWindows = false;
    846         mEventSender.resetMouse();
    847         mEventSender.clearTouchPoints();
    848         mEventSender.clearTouchMetaState();
    849         mPageFinished = false;
    850         mDumpWebKitData = false;
    851         setDefaultWebSettings(mWebView);
    852         mIsGeolocationPermissionSet = false;
    853         mPendingGeolocationPermissionCallbacks = null;
    854         CookieManager.getInstance().removeAllCookie();
    855     }
    856 
    857     private boolean canMoveToNextTest() {
    858         return (mDumpWebKitData && mPageFinished && !mWaitUntilDone) || mTimedOut;
    859     }
    860 
    861     private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) {
    862         if (webview == null) {
    863             return;
    864         }
    865 
    866         setDefaultWebSettings(webview);
    867 
    868         webview.setWebChromeClient(mChromeClient);
    869         webview.setWebViewClient(mViewClient);
    870         // Setting a touch interval of -1 effectively disables the optimisation in WebView
    871         // that stops repeated touch events flooding WebCore. The Event Sender only sends a
    872         // single event rather than a stream of events (like what would generally happen in
    873         // a real use of touch events in a WebView)  and so if the WebView drops the event,
    874         // the test will fail as the test expects one callback for every touch it synthesizes.
    875         WebViewClassic.fromWebView(webview).setTouchInterval(-1);
    876     }
    877 
    878     public void setDefaultWebSettings(WebView webview) {
    879         WebSettingsClassic settings = WebViewClassic.fromWebView(webview).getSettings();
    880         settings.setAppCacheEnabled(true);
    881         settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
    882         settings.setAppCacheMaxSize(Long.MAX_VALUE);
    883         settings.setJavaScriptEnabled(true);
    884         settings.setJavaScriptCanOpenWindowsAutomatically(true);
    885         settings.setSupportMultipleWindows(true);
    886         settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
    887         settings.setDatabaseEnabled(true);
    888         settings.setDatabasePath(getDir("databases",0).getAbsolutePath());
    889         settings.setDomStorageEnabled(true);
    890         settings.setWorkersEnabled(false);
    891         settings.setXSSAuditorEnabled(false);
    892         settings.setPageCacheCapacity(0);
    893         settings.setProperty("use_minimal_memory", "false");
    894         settings.setAllowUniversalAccessFromFileURLs(true);
    895         settings.setAllowFileAccessFromFileURLs(true);
    896     }
    897 
    898     private WebViewClassic mWebViewClassic;
    899     private WebView mWebView;
    900     private WebViewEventSender mEventSender;
    901     private AsyncHandler mHandler;
    902     private TestShellCallback mCallback;
    903 
    904     private CallbackProxy mCallbackProxy;
    905 
    906     private String mTestUrl;
    907     private String mResultFile;
    908     private int mTimeoutInMillis;
    909     private String mUiAutoTestPath;
    910     private BufferedReader mTestListReader;
    911     private int mTotalTestCount;
    912     private int mCurrentTestNumber;
    913     private boolean mStopOnRefError;
    914 
    915     // States
    916     private boolean mTimedOut;
    917     private boolean mRequestedWebKitData;
    918     private boolean mFinishedRunning;
    919 
    920     // Layout test controller variables.
    921     private DumpDataType mDumpDataType;
    922     private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR;
    923     private boolean mDumpTopFrameAsText;
    924     private boolean mDumpChildFramesAsText;
    925     private boolean mWaitUntilDone;
    926     private boolean mDumpTitleChanges;
    927     private StringBuffer mTitleChanges;
    928     private StringBuffer mDialogStrings;
    929     private boolean mKeepWebHistory;
    930     private Vector mWebHistory;
    931     private boolean mDumpDatabaseCallbacks;
    932     private StringBuffer mDatabaseCallbackStrings;
    933     private StringBuffer mConsoleMessages;
    934     private boolean mCanOpenWindows;
    935 
    936     private boolean mPageFinished = false;
    937     private boolean mDumpWebKitData = false;
    938 
    939     static final String TIMEOUT_STR = "**Test timeout";
    940     static final long DUMP_TIMEOUT_MS = 100000; // 100s timeout for dumping webview content
    941 
    942     static final int MSG_TIMEOUT = 0;
    943     static final int MSG_WEBKIT_DATA = 1;
    944     static final int MSG_DUMP_TIMEOUT = 2;
    945 
    946     static final String LOGTAG="TestShell";
    947 
    948     static final String TEST_URL = "TestUrl";
    949     static final String RESULT_FILE = "ResultFile";
    950     static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
    951     static final String UI_AUTO_TEST = "UiAutoTest";
    952     static final String GET_DRAW_TIME = "GetDrawTime";
    953     static final String SAVE_IMAGE = "SaveImage";
    954     static final String TOTAL_TEST_COUNT = "TestCount";
    955     static final String CURRENT_TEST_NUMBER = "TestNumber";
    956     static final String STOP_ON_REF_ERROR = "StopOnReferenceError";
    957 
    958     static final int DRAW_RUNS = 5;
    959     static final String DRAW_TIME_LOG = Environment.getExternalStorageDirectory() +
    960         "/android/page_draw_time.txt";
    961 
    962     private boolean mIsGeolocationPermissionSet;
    963     private boolean mGeolocationPermission;
    964     private Map mPendingGeolocationPermissionCallbacks;
    965 }
    966