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     @Override
    543     public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
    544         WebViewClassic.fromWebView(mWebView).setMockGeolocationPosition(latitude, longitude,
    545                 accuracy);
    546     }
    547 
    548     @Override
    549     public void setMockGeolocationError(int code, String message) {
    550         WebViewClassic.fromWebView(mWebView).setMockGeolocationError(code, message);
    551     }
    552 
    553     @Override
    554     public void setGeolocationPermission(boolean allow) {
    555         Log.v(LOGTAG, "setGeolocationPermission() allow=" + allow);
    556         WebViewClassic.fromWebView(mWebView).setMockGeolocationPermission(allow);
    557     }
    558 
    559     @Override
    560     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
    561             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
    562         WebViewClassic.fromWebView(mWebView).setMockDeviceOrientation(canProvideAlpha, alpha,
    563                 canProvideBeta, beta, canProvideGamma, gamma);
    564     }
    565 
    566     @Override
    567     public void overridePreference(String key, boolean value) {
    568         // TODO: We should look up the correct WebView for the frame which
    569         // called the layoutTestController method. Currently, we just use the
    570         // WebView for the main frame. EventSender suffers from the same
    571         // problem.
    572         if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) {
    573             mWebViewClassic.getSettings().setAppCacheEnabled(value);
    574         } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) {
    575             // Cache the maximum possible number of pages.
    576             mWebViewClassic.getSettings().setPageCacheCapacity(Integer.MAX_VALUE);
    577         } else {
    578             Log.w(LOGTAG, "LayoutTestController.overridePreference(): " +
    579                   "Unsupported preference '" + key + "'");
    580         }
    581     }
    582 
    583     @Override
    584     public void setXSSAuditorEnabled (boolean flag) {
    585         mWebViewClassic.getSettings().setXSSAuditorEnabled(flag);
    586     }
    587 
    588     private final WebViewClient mViewClient = new WebViewClient(){
    589         @Override
    590         public void onPageFinished(WebView view, String url) {
    591             Log.v(LOGTAG, "onPageFinished, url=" + url);
    592             mPageFinished = true;
    593 
    594             // Calling finished() will check if we've met all the conditions for completing
    595             // this test and move to the next one if we are ready. Otherwise we ask WebCore to
    596             // dump the page.
    597             if (finished()) {
    598                 return;
    599             }
    600 
    601             if (!mWaitUntilDone && !mRequestedWebKitData && !mTimedOut) {
    602                 requestWebKitData();
    603             } else {
    604                 if (mWaitUntilDone) {
    605                     Log.v(LOGTAG, "page finished loading but waiting for notifyDone to be called: " + url);
    606                 }
    607 
    608                 if (mRequestedWebKitData) {
    609                     Log.v(LOGTAG, "page finished loading but webkit data has already been requested: " + url);
    610                 }
    611 
    612                 if (mTimedOut) {
    613                     Log.v(LOGTAG, "page finished loading but already timed out: " + url);
    614                 }
    615             }
    616 
    617             super.onPageFinished(view, url);
    618         }
    619 
    620         @Override
    621         public void onPageStarted(WebView view, String url, Bitmap favicon) {
    622             Log.v(LOGTAG, "onPageStarted, url=" + url);
    623             mPageFinished = false;
    624             super.onPageStarted(view, url, favicon);
    625         }
    626 
    627         @Override
    628         public void onReceivedError(WebView view, int errorCode, String description,
    629                 String failingUrl) {
    630             Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
    631                     + ", desc=" + description + ", url=" + failingUrl);
    632             super.onReceivedError(view, errorCode, description, failingUrl);
    633         }
    634 
    635         @Override
    636         public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
    637                 String host, String realm) {
    638             if (handler.useHttpAuthUsernamePassword() && view != null) {
    639                 String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
    640                 if (credentials != null && credentials.length == 2) {
    641                     handler.proceed(credentials[0], credentials[1]);
    642                     return;
    643                 }
    644             }
    645             handler.cancel();
    646         }
    647 
    648         @Override
    649         public void onReceivedSslError(WebView view, SslErrorHandler handler,
    650                 SslError error) {
    651             handler.proceed();
    652         }
    653     };
    654 
    655 
    656     private final WebChromeClient mChromeClient = new WebChromeClient() {
    657         @Override
    658         public void onReceivedTitle(WebView view, String title) {
    659             setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount + ": "+ title);
    660             if (mDumpTitleChanges) {
    661                 mTitleChanges.append("TITLE CHANGED: ");
    662                 mTitleChanges.append(title);
    663                 mTitleChanges.append("\n");
    664             }
    665         }
    666 
    667         @Override
    668         public boolean onJsAlert(WebView view, String url, String message,
    669                 JsResult result) {
    670             if (mDialogStrings == null) {
    671                 mDialogStrings = new StringBuffer();
    672             }
    673             mDialogStrings.append("ALERT: ");
    674             mDialogStrings.append(message);
    675             mDialogStrings.append('\n');
    676             result.confirm();
    677             return true;
    678         }
    679 
    680         @Override
    681         public boolean onJsConfirm(WebView view, String url, String message,
    682                 JsResult result) {
    683             if (mDialogStrings == null) {
    684                 mDialogStrings = new StringBuffer();
    685             }
    686             mDialogStrings.append("CONFIRM: ");
    687             mDialogStrings.append(message);
    688             mDialogStrings.append('\n');
    689             result.confirm();
    690             return true;
    691         }
    692 
    693         @Override
    694         public boolean onJsPrompt(WebView view, String url, String message,
    695                 String defaultValue, JsPromptResult result) {
    696             if (mDialogStrings == null) {
    697                 mDialogStrings = new StringBuffer();
    698             }
    699             mDialogStrings.append("PROMPT: ");
    700             mDialogStrings.append(message);
    701             mDialogStrings.append(", default text: ");
    702             mDialogStrings.append(defaultValue);
    703             mDialogStrings.append('\n');
    704             result.confirm();
    705             return true;
    706         }
    707 
    708         @Override
    709         public boolean onJsTimeout() {
    710             Log.v(LOGTAG, "JavaScript timeout");
    711             return false;
    712         }
    713 
    714         @Override
    715         public void onExceededDatabaseQuota(String url_str,
    716                 String databaseIdentifier, long currentQuota,
    717                 long estimatedSize, long totalUsedQuota,
    718                 WebStorage.QuotaUpdater callback) {
    719             if (mDumpDatabaseCallbacks) {
    720                 if (mDatabaseCallbackStrings == null) {
    721                     mDatabaseCallbackStrings = new StringBuffer();
    722                 }
    723 
    724                 String protocol = "";
    725                 String host = "";
    726                 int port = 0;
    727 
    728                 try {
    729                     URL url = new URL(url_str);
    730                     protocol = url.getProtocol();
    731                     host = url.getHost();
    732                     if (url.getPort() > -1) {
    733                         port = url.getPort();
    734                     }
    735                 } catch (MalformedURLException e) {}
    736 
    737                 String databaseCallbackString =
    738                         "UI DELEGATE DATABASE CALLBACK: " +
    739                         "exceededDatabaseQuotaForSecurityOrigin:{" + protocol +
    740                         ", " + host + ", " + port + "} database:" +
    741                         databaseIdentifier + "\n";
    742                 Log.v(LOGTAG, "LOG: "+databaseCallbackString);
    743                 mDatabaseCallbackStrings.append(databaseCallbackString);
    744             }
    745             // Give 5MB more quota.
    746             callback.updateQuota(currentQuota + 1024 * 1024 * 5);
    747         }
    748 
    749         @Override
    750         public void onGeolocationPermissionsShowPrompt(String origin,
    751                 GeolocationPermissions.Callback callback) {
    752             throw new RuntimeException(
    753                     "The WebCore mock used by DRT should bypass the usual permissions flow.");
    754         }
    755 
    756         @Override
    757         public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
    758             String msg = "CONSOLE MESSAGE: line " + consoleMessage.lineNumber() + ": "
    759                     + consoleMessage.message() + "\n";
    760             if (mConsoleMessages == null) {
    761                 mConsoleMessages = new StringBuffer();
    762             }
    763             mConsoleMessages.append(msg);
    764             Log.v(LOGTAG, "LOG: " + msg);
    765             // the rationale here is that if there's an error of either type, and the test was
    766             // waiting for "notifyDone" signal to finish, then there's no point in waiting
    767             // anymore because the JS execution is already terminated at this point and a
    768             // "notifyDone" will never come out so it's just wasting time till timeout kicks in
    769             if ((msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:"))
    770                     && mWaitUntilDone && mStopOnRefError) {
    771                 Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError.");
    772                 mHandler.postDelayed(new Runnable() {
    773                     @Override
    774                     public void run() {
    775                         notifyDone();
    776                     }
    777                 }, 500);
    778             }
    779             return true;
    780         }
    781 
    782         @Override
    783         public boolean onCreateWindow(WebView view, boolean dialog,
    784                 boolean userGesture, Message resultMsg) {
    785             if (!mCanOpenWindows) {
    786                 // We can't open windows, so just send null back.
    787                 WebView.WebViewTransport transport =
    788                         (WebView.WebViewTransport) resultMsg.obj;
    789                 transport.setWebView(null);
    790                 resultMsg.sendToTarget();
    791                 return true;
    792             }
    793 
    794             // We never display the new window, just create the view and
    795             // allow it's content to execute and be recorded by the test
    796             // runner.
    797 
    798             HashMap<String, Object> jsIfaces = new HashMap<String, Object>();
    799             jsIfaces.put("layoutTestController", mCallbackProxy);
    800             jsIfaces.put("eventSender", mCallbackProxy);
    801             WebView newWindowView = new NewWindowWebView(TestShellActivity.this, jsIfaces);
    802             setupWebViewForLayoutTests(newWindowView, mCallbackProxy);
    803             WebView.WebViewTransport transport =
    804                     (WebView.WebViewTransport) resultMsg.obj;
    805             transport.setWebView(newWindowView);
    806             resultMsg.sendToTarget();
    807             return true;
    808         }
    809 
    810         @Override
    811         public void onCloseWindow(WebView view) {
    812             view.destroy();
    813         }
    814     };
    815 
    816     private static class NewWindowWebView extends WebView {
    817         public NewWindowWebView(Context context, Map<String, Object> jsIfaces) {
    818             super(context, null, 0, jsIfaces, false);
    819         }
    820     }
    821 
    822     private void resetTestStatus() {
    823         mWaitUntilDone = false;
    824         mDumpDataType = mDefaultDumpDataType;
    825         mDumpTopFrameAsText = false;
    826         mDumpChildFramesAsText = false;
    827         mTimedOut = false;
    828         mDumpTitleChanges = false;
    829         mRequestedWebKitData = false;
    830         mDumpDatabaseCallbacks = false;
    831         mCanOpenWindows = false;
    832         mEventSender.resetMouse();
    833         mEventSender.clearTouchPoints();
    834         mEventSender.clearTouchMetaState();
    835         mPageFinished = false;
    836         mDumpWebKitData = false;
    837         setDefaultWebSettings(mWebView);
    838         CookieManager.getInstance().removeAllCookie();
    839         mWebViewClassic.setUseMockGeolocation();
    840     }
    841 
    842     private boolean canMoveToNextTest() {
    843         return (mDumpWebKitData && mPageFinished && !mWaitUntilDone) || mTimedOut;
    844     }
    845 
    846     private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) {
    847         if (webview == null) {
    848             return;
    849         }
    850 
    851         setDefaultWebSettings(webview);
    852 
    853         webview.setWebChromeClient(mChromeClient);
    854         webview.setWebViewClient(mViewClient);
    855         // Setting a touch interval of -1 effectively disables the optimisation in WebView
    856         // that stops repeated touch events flooding WebCore. The Event Sender only sends a
    857         // single event rather than a stream of events (like what would generally happen in
    858         // a real use of touch events in a WebView)  and so if the WebView drops the event,
    859         // the test will fail as the test expects one callback for every touch it synthesizes.
    860         WebViewClassic.fromWebView(webview).setTouchInterval(-1);
    861     }
    862 
    863     public void setDefaultWebSettings(WebView webview) {
    864         WebSettingsClassic settings = WebViewClassic.fromWebView(webview).getSettings();
    865         settings.setAppCacheEnabled(true);
    866         settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
    867         settings.setAppCacheMaxSize(Long.MAX_VALUE);
    868         settings.setJavaScriptEnabled(true);
    869         settings.setJavaScriptCanOpenWindowsAutomatically(true);
    870         settings.setSupportMultipleWindows(true);
    871         settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
    872         settings.setDatabaseEnabled(true);
    873         settings.setDatabasePath(getDir("databases",0).getAbsolutePath());
    874         settings.setDomStorageEnabled(true);
    875         settings.setWorkersEnabled(false);
    876         settings.setXSSAuditorEnabled(false);
    877         settings.setPageCacheCapacity(0);
    878         settings.setProperty("use_minimal_memory", "false");
    879         settings.setAllowUniversalAccessFromFileURLs(true);
    880         settings.setAllowFileAccessFromFileURLs(true);
    881     }
    882 
    883     private WebViewClassic mWebViewClassic;
    884     private WebView mWebView;
    885     private WebViewEventSender mEventSender;
    886     private AsyncHandler mHandler;
    887     private TestShellCallback mCallback;
    888 
    889     private CallbackProxy mCallbackProxy;
    890 
    891     private String mTestUrl;
    892     private String mResultFile;
    893     private int mTimeoutInMillis;
    894     private String mUiAutoTestPath;
    895     private BufferedReader mTestListReader;
    896     private int mTotalTestCount;
    897     private int mCurrentTestNumber;
    898     private boolean mStopOnRefError;
    899 
    900     // States
    901     private boolean mTimedOut;
    902     private boolean mRequestedWebKitData;
    903     private boolean mFinishedRunning;
    904 
    905     // Layout test controller variables.
    906     private DumpDataType mDumpDataType;
    907     private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR;
    908     private boolean mDumpTopFrameAsText;
    909     private boolean mDumpChildFramesAsText;
    910     private boolean mWaitUntilDone;
    911     private boolean mDumpTitleChanges;
    912     private StringBuffer mTitleChanges;
    913     private StringBuffer mDialogStrings;
    914     private boolean mKeepWebHistory;
    915     private Vector mWebHistory;
    916     private boolean mDumpDatabaseCallbacks;
    917     private StringBuffer mDatabaseCallbackStrings;
    918     private StringBuffer mConsoleMessages;
    919     private boolean mCanOpenWindows;
    920 
    921     private boolean mPageFinished = false;
    922     private boolean mDumpWebKitData = false;
    923 
    924     static final String TIMEOUT_STR = "**Test timeout";
    925     static final long DUMP_TIMEOUT_MS = 100000; // 100s timeout for dumping webview content
    926 
    927     static final int MSG_TIMEOUT = 0;
    928     static final int MSG_WEBKIT_DATA = 1;
    929     static final int MSG_DUMP_TIMEOUT = 2;
    930 
    931     static final String LOGTAG="TestShell";
    932 
    933     static final String TEST_URL = "TestUrl";
    934     static final String RESULT_FILE = "ResultFile";
    935     static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
    936     static final String UI_AUTO_TEST = "UiAutoTest";
    937     static final String GET_DRAW_TIME = "GetDrawTime";
    938     static final String SAVE_IMAGE = "SaveImage";
    939     static final String TOTAL_TEST_COUNT = "TestCount";
    940     static final String CURRENT_TEST_NUMBER = "TestNumber";
    941     static final String STOP_ON_REF_ERROR = "StopOnReferenceError";
    942 
    943     static final int DRAW_RUNS = 5;
    944     static final String DRAW_TIME_LOG = Environment.getExternalStorageDirectory() +
    945         "/android/page_draw_time.txt";
    946 }
    947