Home | History | Annotate | Download | only in dumprendertree2
      1 /*
      2  * Copyright (C) 2010 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.dumprendertree2;
     18 
     19 import android.app.Activity;
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.ServiceConnection;
     24 import android.net.http.SslError;
     25 import android.os.Bundle;
     26 import android.os.Handler;
     27 import android.os.IBinder;
     28 import android.os.Message;
     29 import android.os.Messenger;
     30 import android.os.PowerManager;
     31 import android.os.PowerManager.WakeLock;
     32 import android.os.Process;
     33 import android.os.RemoteException;
     34 import android.util.Log;
     35 import android.view.Window;
     36 import android.webkit.ConsoleMessage;
     37 import android.webkit.GeolocationPermissions;
     38 import android.webkit.HttpAuthHandler;
     39 import android.webkit.JsPromptResult;
     40 import android.webkit.JsResult;
     41 import android.webkit.SslErrorHandler;
     42 import android.webkit.WebChromeClient;
     43 import android.webkit.WebSettings;
     44 import android.webkit.WebStorage;
     45 import android.webkit.WebStorage.QuotaUpdater;
     46 import android.webkit.WebView;
     47 import android.webkit.WebViewClient;
     48 
     49 import java.lang.Thread.UncaughtExceptionHandler;
     50 import java.util.HashMap;
     51 import java.util.Iterator;
     52 import java.util.List;
     53 import java.util.Map;
     54 
     55 /**
     56  * This activity executes the test. It contains WebView and logic of LayoutTestController
     57  * functions. It runs in a separate process and sends the results of running the test
     58  * to ManagerService. The reason why is to handle crashing (test that crashes brings down
     59  * whole process with it).
     60  */
     61 public class LayoutTestsExecutor extends Activity {
     62 
     63     private enum CurrentState {
     64         IDLE,
     65         RENDERING_PAGE,
     66         WAITING_FOR_ASYNCHRONOUS_TEST,
     67         OBTAINING_RESULT;
     68 
     69         public boolean isRunningState() {
     70             return this == CurrentState.RENDERING_PAGE ||
     71                     this == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
     72         }
     73     }
     74 
     75     private static final String LOG_TAG = "LayoutTestsExecutor";
     76 
     77     public static final String EXTRA_TESTS_FILE = "TestsList";
     78     public static final String EXTRA_TEST_INDEX = "TestIndex";
     79 
     80     private static final int MSG_ACTUAL_RESULT_OBTAINED = 0;
     81     private static final int MSG_TEST_TIMED_OUT = 1;
     82 
     83     private static final int DEFAULT_TIME_OUT_MS = 15 * 1000;
     84 
     85     /** A list of tests that remain to run since last crash */
     86     private List<String> mTestsList;
     87 
     88     /**
     89      * This is a number of currently running test. It is 0-based and doesn't reset after
     90      * the crash. Initial index is passed to LayoutTestsExecuter in the intent that starts
     91      * it.
     92      */
     93     private int mCurrentTestIndex;
     94 
     95     /** The total number of tests to run, doesn't reset after crash */
     96     private int mTotalTestCount;
     97 
     98     private WebView mCurrentWebView;
     99     private String mCurrentTestRelativePath;
    100     private String mCurrentTestUri;
    101     private CurrentState mCurrentState = CurrentState.IDLE;
    102 
    103     private boolean mCurrentTestTimedOut;
    104     private AbstractResult mCurrentResult;
    105     private AdditionalTextOutput mCurrentAdditionalTextOutput;
    106 
    107     private LayoutTestController mLayoutTestController = new LayoutTestController(this);
    108     private boolean mCanOpenWindows;
    109     private boolean mDumpDatabaseCallbacks;
    110     private boolean mIsGeolocationPermissionSet;
    111     private boolean mGeolocationPermission;
    112     private Map<GeolocationPermissions.Callback, String> mPendingGeolocationPermissionCallbacks;
    113 
    114     private EventSender mEventSender = new EventSender();
    115 
    116     private WakeLock mScreenDimLock;
    117 
    118     /** COMMUNICATION WITH ManagerService */
    119 
    120     private Messenger mManagerServiceMessenger;
    121 
    122     private ServiceConnection mServiceConnection = new ServiceConnection() {
    123 
    124         @Override
    125         public void onServiceConnected(ComponentName name, IBinder service) {
    126             mManagerServiceMessenger = new Messenger(service);
    127             startTests();
    128         }
    129 
    130         @Override
    131         public void onServiceDisconnected(ComponentName name) {
    132             /** TODO */
    133         }
    134     };
    135 
    136     private final Handler mResultHandler = new Handler() {
    137         @Override
    138         public void handleMessage(Message msg) {
    139             switch (msg.what) {
    140                 case MSG_ACTUAL_RESULT_OBTAINED:
    141                     onActualResultsObtained();
    142                     break;
    143 
    144                 case MSG_TEST_TIMED_OUT:
    145                     onTestTimedOut();
    146                     break;
    147 
    148                 default:
    149                     break;
    150             }
    151         }
    152     };
    153 
    154     /** WEBVIEW CONFIGURATION */
    155 
    156     private WebViewClient mWebViewClient = new WebViewClient() {
    157         @Override
    158         public void onPageFinished(WebView view, String url) {
    159             /** Some tests fire up many page loads, we don't want to detect them */
    160             if (!url.equals(mCurrentTestUri)) {
    161                 return;
    162             }
    163 
    164             if (mCurrentState == CurrentState.RENDERING_PAGE) {
    165                 onTestFinished();
    166             }
    167         }
    168 
    169          @Override
    170          public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
    171                  String host, String realm) {
    172              if (handler.useHttpAuthUsernamePassword() && view != null) {
    173                  String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
    174                  if (credentials != null && credentials.length == 2) {
    175                      handler.proceed(credentials[0], credentials[1]);
    176                      return;
    177                  }
    178              }
    179              handler.cancel();
    180          }
    181 
    182          @Override
    183          public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    184              // We ignore SSL errors. In particular, the certificate used by the LayoutTests server
    185              // produces an error as it lacks a CN field.
    186              handler.proceed();
    187          }
    188     };
    189 
    190     private WebChromeClient mWebChromeClient = new WebChromeClient() {
    191         @Override
    192         public void onExceededDatabaseQuota(String url, String databaseIdentifier,
    193                 long currentQuota, long estimatedSize, long totalUsedQuota,
    194                 QuotaUpdater quotaUpdater) {
    195             /** TODO: This should be recorded as part of the text result */
    196             /** TODO: The quota should also probably be reset somehow for every test? */
    197             if (mDumpDatabaseCallbacks) {
    198                 getCurrentAdditionalTextOutput().appendExceededDbQuotaMessage(url,
    199                         databaseIdentifier);
    200             }
    201             quotaUpdater.updateQuota(currentQuota + 5 * 1024 * 1024);
    202         }
    203 
    204         @Override
    205         public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
    206             getCurrentAdditionalTextOutput().appendJsAlert(message);
    207             result.confirm();
    208             return true;
    209         }
    210 
    211         @Override
    212         public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
    213             getCurrentAdditionalTextOutput().appendJsConfirm(message);
    214             result.confirm();
    215             return true;
    216         }
    217 
    218         @Override
    219         public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
    220                 JsPromptResult result) {
    221             getCurrentAdditionalTextOutput().appendJsPrompt(message, defaultValue);
    222             result.confirm();
    223             return true;
    224         }
    225 
    226         @Override
    227         public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
    228             getCurrentAdditionalTextOutput().appendConsoleMessage(consoleMessage);
    229             return true;
    230         }
    231 
    232         @Override
    233         public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture,
    234                 Message resultMsg) {
    235             WebView.WebViewTransport transport = (WebView.WebViewTransport)resultMsg.obj;
    236             /** By default windows cannot be opened, so just send null back. */
    237             WebView newWindowWebView = null;
    238 
    239             if (mCanOpenWindows) {
    240                 /**
    241                  * We never display the new window, just create the view and allow it's content to
    242                  * execute and be recorded by the executor.
    243                  */
    244                 newWindowWebView = createWebViewWithJavascriptInterfaces();
    245                 setupWebView(newWindowWebView);
    246             }
    247 
    248             transport.setWebView(newWindowWebView);
    249             resultMsg.sendToTarget();
    250             return true;
    251         }
    252 
    253         @Override
    254         public void onGeolocationPermissionsShowPrompt(String origin,
    255                 GeolocationPermissions.Callback callback) {
    256             if (mIsGeolocationPermissionSet) {
    257                 callback.invoke(origin, mGeolocationPermission, false);
    258                 return;
    259             }
    260             if (mPendingGeolocationPermissionCallbacks == null) {
    261                 mPendingGeolocationPermissionCallbacks =
    262                         new HashMap<GeolocationPermissions.Callback, String>();
    263             }
    264             mPendingGeolocationPermissionCallbacks.put(callback, origin);
    265         }
    266     };
    267 
    268     /** IMPLEMENTATION */
    269 
    270     @Override
    271     protected void onCreate(Bundle savedInstanceState) {
    272         super.onCreate(savedInstanceState);
    273 
    274         /**
    275          * It detects the crash by catching all the uncaught exceptions. However, we
    276          * still have to kill the process, because after catching the exception the
    277          * activity remains in a strange state, where intents don't revive it.
    278          * However, we send the message to the service to speed up the rebooting
    279          * (we don't have to wait for time-out to kick in).
    280          */
    281         Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
    282             @Override
    283             public void uncaughtException(Thread thread, Throwable e) {
    284                 Log.w(LOG_TAG,
    285                         "onTestCrashed(): " + mCurrentTestRelativePath + " thread=" + thread, e);
    286 
    287                 try {
    288                     Message serviceMsg =
    289                             Message.obtain(null, ManagerService.MSG_CURRENT_TEST_CRASHED);
    290 
    291                     mManagerServiceMessenger.send(serviceMsg);
    292                 } catch (RemoteException e2) {
    293                     Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e2);
    294                 }
    295 
    296                 Process.killProcess(Process.myPid());
    297             }
    298         });
    299 
    300         requestWindowFeature(Window.FEATURE_PROGRESS);
    301 
    302         Intent intent = getIntent();
    303         mTestsList = FsUtils.loadTestListFromStorage(intent.getStringExtra(EXTRA_TESTS_FILE));
    304         mCurrentTestIndex = intent.getIntExtra(EXTRA_TEST_INDEX, -1);
    305         mTotalTestCount = mCurrentTestIndex + mTestsList.size();
    306 
    307         PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    308         mScreenDimLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
    309                 | PowerManager.ON_AFTER_RELEASE, "WakeLock in LayoutTester");
    310         mScreenDimLock.acquire();
    311 
    312         bindService(new Intent(this, ManagerService.class), mServiceConnection,
    313                 Context.BIND_AUTO_CREATE);
    314     }
    315 
    316     private void reset() {
    317         WebView previousWebView = mCurrentWebView;
    318 
    319         resetLayoutTestController();
    320 
    321         mCurrentTestTimedOut = false;
    322         mCurrentResult = null;
    323         mCurrentAdditionalTextOutput = null;
    324 
    325         mCurrentWebView = createWebViewWithJavascriptInterfaces();
    326         // When we create the first WebView, we need to pause to wait for the WebView thread to spin
    327         // and up and for it to register its message handlers.
    328         if (previousWebView == null) {
    329             try {
    330                 Thread.currentThread().sleep(1000);
    331             } catch (Exception e) {}
    332         }
    333         setupWebView(mCurrentWebView);
    334 
    335         mEventSender.reset(mCurrentWebView);
    336 
    337         setContentView(mCurrentWebView);
    338         if (previousWebView != null) {
    339             Log.d(LOG_TAG + "::reset", "previousWebView != null");
    340             previousWebView.destroy();
    341         }
    342     }
    343 
    344     private static class WebViewWithJavascriptInterfaces extends WebView {
    345         public WebViewWithJavascriptInterfaces(
    346                 Context context, Map<String, Object> javascriptInterfaces) {
    347             super(context,
    348                   null, // attribute set
    349                   0, // default style resource ID
    350                   javascriptInterfaces,
    351                   false); // is private browsing
    352         }
    353     }
    354     private WebView createWebViewWithJavascriptInterfaces() {
    355         Map<String, Object> javascriptInterfaces = new HashMap<String, Object>();
    356         javascriptInterfaces.put("layoutTestController", mLayoutTestController);
    357         javascriptInterfaces.put("eventSender", mEventSender);
    358         return new WebViewWithJavascriptInterfaces(this, javascriptInterfaces);
    359     }
    360 
    361     private void setupWebView(WebView webView) {
    362         webView.setWebViewClient(mWebViewClient);
    363         webView.setWebChromeClient(mWebChromeClient);
    364 
    365         /**
    366          * Setting a touch interval of -1 effectively disables the optimisation in WebView
    367          * that stops repeated touch events flooding WebCore. The Event Sender only sends a
    368          * single event rather than a stream of events (like what would generally happen in
    369          * a real use of touch events in a WebView)  and so if the WebView drops the event,
    370          * the test will fail as the test expects one callback for every touch it synthesizes.
    371          */
    372         webView.setTouchInterval(-1);
    373 
    374         webView.clearCache(true);
    375 
    376         WebSettings webViewSettings = webView.getSettings();
    377         webViewSettings.setAppCacheEnabled(true);
    378         webViewSettings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
    379         // Use of larger values causes unexplained AppCache database corruption.
    380         // TODO: Investigate what's really going on here.
    381         webViewSettings.setAppCacheMaxSize(100 * 1024 * 1024);
    382         webViewSettings.setJavaScriptEnabled(true);
    383         webViewSettings.setJavaScriptCanOpenWindowsAutomatically(true);
    384         webViewSettings.setSupportMultipleWindows(true);
    385         webViewSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
    386         webViewSettings.setDatabaseEnabled(true);
    387         webViewSettings.setDatabasePath(getDir("databases", 0).getAbsolutePath());
    388         webViewSettings.setDomStorageEnabled(true);
    389         webViewSettings.setWorkersEnabled(false);
    390         webViewSettings.setXSSAuditorEnabled(false);
    391         webViewSettings.setPageCacheCapacity(0);
    392 
    393         // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
    394         mCurrentWebView.useMockDeviceOrientation();
    395 
    396         // Must do this after setting the AppCache path.
    397         WebStorage.getInstance().deleteAllData();
    398     }
    399 
    400     private void startTests() {
    401         // This is called when the tests are started and after each crash.
    402         // We only send the reset message in the former case.
    403         if (mCurrentTestIndex <= 0) {
    404             sendResetMessage();
    405         }
    406         if (mCurrentTestIndex == 0) {
    407             sendFirstTestMessage();
    408         }
    409 
    410         runNextTest();
    411     }
    412 
    413     private void sendResetMessage() {
    414         try {
    415             Message serviceMsg = Message.obtain(null, ManagerService.MSG_RESET);
    416             mManagerServiceMessenger.send(serviceMsg);
    417         } catch (RemoteException e) {
    418             Log.e(LOG_TAG, "Error sending message to manager service:", e);
    419         }
    420     }
    421 
    422     private void sendFirstTestMessage() {
    423         try {
    424             Message serviceMsg = Message.obtain(null, ManagerService.MSG_FIRST_TEST);
    425 
    426             Bundle bundle = new Bundle();
    427             bundle.putString("firstTest", mTestsList.get(0));
    428             bundle.putInt("index", mCurrentTestIndex);
    429 
    430             serviceMsg.setData(bundle);
    431             mManagerServiceMessenger.send(serviceMsg);
    432         } catch (RemoteException e) {
    433             Log.e(LOG_TAG, "Error sending message to manager service:", e);
    434         }
    435     }
    436 
    437     private void runNextTest() {
    438         assert mCurrentState == CurrentState.IDLE : "mCurrentState = " + mCurrentState.name();
    439 
    440         if (mTestsList.isEmpty()) {
    441             onAllTestsFinished();
    442             return;
    443         }
    444 
    445         mCurrentTestRelativePath = mTestsList.remove(0);
    446 
    447         Log.i(LOG_TAG, "runNextTest(): Start: " + mCurrentTestRelativePath +
    448                 " (" + mCurrentTestIndex + ")");
    449 
    450         mCurrentTestUri = FileFilter.getUrl(mCurrentTestRelativePath, true).toString();
    451 
    452         reset();
    453 
    454         /** Start time-out countdown and the test */
    455         mCurrentState = CurrentState.RENDERING_PAGE;
    456         mResultHandler.sendEmptyMessageDelayed(MSG_TEST_TIMED_OUT, DEFAULT_TIME_OUT_MS);
    457         mCurrentWebView.loadUrl(mCurrentTestUri);
    458     }
    459 
    460     private void onTestTimedOut() {
    461         assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
    462 
    463         Log.w(LOG_TAG, "onTestTimedOut(): " + mCurrentTestRelativePath);
    464         mCurrentTestTimedOut = true;
    465 
    466         /**
    467          * While it is theoretically possible that the test times out because
    468          * of webview becoming unresponsive, it is very unlikely. Therefore it's
    469          * assumed that obtaining results (that calls various webview methods)
    470          * will not itself hang.
    471          */
    472         obtainActualResultsFromWebView();
    473     }
    474 
    475     private void onTestFinished() {
    476         assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
    477 
    478         Log.i(LOG_TAG, "onTestFinished(): " + mCurrentTestRelativePath);
    479         mResultHandler.removeMessages(MSG_TEST_TIMED_OUT);
    480         obtainActualResultsFromWebView();
    481     }
    482 
    483     private void obtainActualResultsFromWebView() {
    484         /**
    485          * If the result has not been set by the time the test finishes we create
    486          * a default type of result.
    487          */
    488         if (mCurrentResult == null) {
    489             /** TODO: Default type should be RenderTreeResult. We don't support it now. */
    490             mCurrentResult = new TextResult(mCurrentTestRelativePath);
    491         }
    492 
    493         mCurrentState = CurrentState.OBTAINING_RESULT;
    494 
    495         if (mCurrentTestTimedOut) {
    496             mCurrentResult.setDidTimeOut();
    497         }
    498         mCurrentResult.obtainActualResults(mCurrentWebView,
    499                 mResultHandler.obtainMessage(MSG_ACTUAL_RESULT_OBTAINED));
    500     }
    501 
    502     private void onActualResultsObtained() {
    503         assert mCurrentState == CurrentState.OBTAINING_RESULT
    504                 : "mCurrentState = " + mCurrentState.name();
    505 
    506         Log.i(LOG_TAG, "onActualResultsObtained(): " + mCurrentTestRelativePath);
    507         mCurrentState = CurrentState.IDLE;
    508 
    509         reportResultToService();
    510         mCurrentTestIndex++;
    511         updateProgressBar();
    512         runNextTest();
    513     }
    514 
    515     private void reportResultToService() {
    516         if (mCurrentAdditionalTextOutput != null) {
    517             mCurrentResult.setAdditionalTextOutputString(mCurrentAdditionalTextOutput.toString());
    518         }
    519 
    520         try {
    521             Message serviceMsg =
    522                     Message.obtain(null, ManagerService.MSG_PROCESS_ACTUAL_RESULTS);
    523 
    524             Bundle bundle = mCurrentResult.getBundle();
    525             bundle.putInt("testIndex", mCurrentTestIndex);
    526             if (!mTestsList.isEmpty()) {
    527                 bundle.putString("nextTest", mTestsList.get(0));
    528             }
    529 
    530             serviceMsg.setData(bundle);
    531             mManagerServiceMessenger.send(serviceMsg);
    532         } catch (RemoteException e) {
    533             Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
    534         }
    535     }
    536 
    537     private void updateProgressBar() {
    538         getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
    539                 mCurrentTestIndex * Window.PROGRESS_END / mTotalTestCount);
    540         setTitle(mCurrentTestIndex * 100 / mTotalTestCount + "% " +
    541                 "(" + mCurrentTestIndex + "/" + mTotalTestCount + ")");
    542     }
    543 
    544     private void onAllTestsFinished() {
    545         mScreenDimLock.release();
    546 
    547         try {
    548             Message serviceMsg =
    549                     Message.obtain(null, ManagerService.MSG_ALL_TESTS_FINISHED);
    550             mManagerServiceMessenger.send(serviceMsg);
    551         } catch (RemoteException e) {
    552             Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
    553         }
    554 
    555         unbindService(mServiceConnection);
    556     }
    557 
    558     private AdditionalTextOutput getCurrentAdditionalTextOutput() {
    559         if (mCurrentAdditionalTextOutput == null) {
    560             mCurrentAdditionalTextOutput = new AdditionalTextOutput();
    561         }
    562         return mCurrentAdditionalTextOutput;
    563     }
    564 
    565     /** LAYOUT TEST CONTROLLER */
    566 
    567     private static final int MSG_WAIT_UNTIL_DONE = 0;
    568     private static final int MSG_NOTIFY_DONE = 1;
    569     private static final int MSG_DUMP_AS_TEXT = 2;
    570     private static final int MSG_DUMP_CHILD_FRAMES_AS_TEXT = 3;
    571     private static final int MSG_SET_CAN_OPEN_WINDOWS = 4;
    572     private static final int MSG_DUMP_DATABASE_CALLBACKS = 5;
    573     private static final int MSG_SET_GEOLOCATION_PERMISSION = 6;
    574     private static final int MSG_OVERRIDE_PREFERENCE = 7;
    575     private static final int MSG_SET_XSS_AUDITOR_ENABLED = 8;
    576 
    577     /** String constants for use with layoutTestController.overridePreference() */
    578     private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED =
    579             "WebKitOfflineWebApplicationCacheEnabled";
    580     private final String WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY = "WebKitUsesPageCachePreferenceKey";
    581 
    582     Handler mLayoutTestControllerHandler = new Handler() {
    583         @Override
    584         public void handleMessage(Message msg) {
    585             assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
    586 
    587             switch (msg.what) {
    588                 case MSG_DUMP_AS_TEXT:
    589                     if (mCurrentResult == null) {
    590                         mCurrentResult = new TextResult(mCurrentTestRelativePath);
    591                     }
    592                     assert mCurrentResult instanceof TextResult
    593                             : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
    594                     break;
    595 
    596                 case MSG_DUMP_CHILD_FRAMES_AS_TEXT:
    597                     /** If dumpAsText was not called we assume that the result should be text */
    598                     if (mCurrentResult == null) {
    599                         mCurrentResult = new TextResult(mCurrentTestRelativePath);
    600                     }
    601 
    602                     assert mCurrentResult instanceof TextResult
    603                             : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
    604 
    605                     ((TextResult)mCurrentResult).setDumpChildFramesAsText(true);
    606                     break;
    607 
    608                 case MSG_DUMP_DATABASE_CALLBACKS:
    609                     mDumpDatabaseCallbacks = true;
    610                     break;
    611 
    612                 case MSG_NOTIFY_DONE:
    613                     if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
    614                         onTestFinished();
    615                     }
    616                     break;
    617 
    618                 case MSG_OVERRIDE_PREFERENCE:
    619                     /**
    620                      * TODO: We should look up the correct WebView for the frame which
    621                      * called the layoutTestController method. Currently, we just use the
    622                      * WebView for the main frame. EventSender suffers from the same
    623                      * problem.
    624                      */
    625                     String key = msg.getData().getString("key");
    626                     boolean value = msg.getData().getBoolean("value");
    627                     if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) {
    628                         mCurrentWebView.getSettings().setAppCacheEnabled(value);
    629                     } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) {
    630                         // Cache the maximum possible number of pages.
    631                         mCurrentWebView.getSettings().setPageCacheCapacity(Integer.MAX_VALUE);
    632                     } else {
    633                         Log.w(LOG_TAG, "LayoutTestController.overridePreference(): " +
    634                               "Unsupported preference '" + key + "'");
    635                     }
    636                     break;
    637 
    638                 case MSG_SET_CAN_OPEN_WINDOWS:
    639                     mCanOpenWindows = true;
    640                     break;
    641 
    642                 case MSG_SET_GEOLOCATION_PERMISSION:
    643                     mIsGeolocationPermissionSet = true;
    644                     mGeolocationPermission = msg.arg1 == 1;
    645 
    646                     if (mPendingGeolocationPermissionCallbacks != null) {
    647                         Iterator<GeolocationPermissions.Callback> iter =
    648                                 mPendingGeolocationPermissionCallbacks.keySet().iterator();
    649                         while (iter.hasNext()) {
    650                             GeolocationPermissions.Callback callback = iter.next();
    651                             String origin = mPendingGeolocationPermissionCallbacks.get(callback);
    652                             callback.invoke(origin, mGeolocationPermission, false);
    653                         }
    654                         mPendingGeolocationPermissionCallbacks = null;
    655                     }
    656                     break;
    657 
    658                 case MSG_SET_XSS_AUDITOR_ENABLED:
    659                     mCurrentWebView.getSettings().setXSSAuditorEnabled(msg.arg1 == 1);
    660                     break;
    661 
    662                 case MSG_WAIT_UNTIL_DONE:
    663                     mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
    664                     break;
    665 
    666                 default:
    667                     assert false : "msg.what=" + msg.what;
    668                     break;
    669             }
    670         }
    671     };
    672 
    673     private void resetLayoutTestController() {
    674         mCanOpenWindows = false;
    675         mDumpDatabaseCallbacks = false;
    676         mIsGeolocationPermissionSet = false;
    677         mPendingGeolocationPermissionCallbacks = null;
    678     }
    679 
    680     public void dumpAsText(boolean enablePixelTest) {
    681         Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpAsText(" + enablePixelTest + ") called");
    682         /** TODO: Implement */
    683         if (enablePixelTest) {
    684             Log.w(LOG_TAG, "enablePixelTest not implemented, switching to false");
    685         }
    686         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_AS_TEXT);
    687     }
    688 
    689     public void dumpChildFramesAsText() {
    690         Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpChildFramesAsText() called");
    691         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
    692     }
    693 
    694     public void dumpDatabaseCallbacks() {
    695         Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
    696         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
    697     }
    698 
    699     public void notifyDone() {
    700         Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
    701         mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
    702     }
    703 
    704     public void overridePreference(String key, boolean value) {
    705         Log.i(LOG_TAG, mCurrentTestRelativePath + ": overridePreference(" + key + ", " + value +
    706         ") called");
    707         Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_OVERRIDE_PREFERENCE);
    708         msg.getData().putString("key", key);
    709         msg.getData().putBoolean("value", value);
    710         msg.sendToTarget();
    711     }
    712 
    713     public void setCanOpenWindows() {
    714         Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
    715         mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
    716     }
    717 
    718     public void setGeolocationPermission(boolean allow) {
    719         Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
    720                 ") called");
    721         Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
    722         msg.arg1 = allow ? 1 : 0;
    723         msg.sendToTarget();
    724     }
    725 
    726     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
    727             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
    728         Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
    729                 ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
    730                 ", " + gamma + ")");
    731         mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
    732                 canProvideGamma, gamma);
    733     }
    734 
    735     public void setXSSAuditorEnabled(boolean flag) {
    736         Log.i(LOG_TAG, mCurrentTestRelativePath + ": setXSSAuditorEnabled(" + flag + ") called");
    737         Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_XSS_AUDITOR_ENABLED);
    738         msg.arg1 = flag ? 1 : 0;
    739         msg.sendToTarget();
    740     }
    741 
    742     public void waitUntilDone() {
    743         Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
    744         mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
    745     }
    746 
    747 }
    748