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