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