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