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