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