LayoutTestsExecutor.java revision f09aafa27503becdcbe2244356c2c7505dff8fe4
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        setupWebView(mCurrentWebView);
331
332        mEventSender.reset(mCurrentWebView);
333
334        setContentView(mCurrentWebView);
335        if (previousWebView != null) {
336            Log.d(LOG_TAG + "::reset", "previousWebView != null");
337            previousWebView.destroy();
338        }
339    }
340
341    private static class WebViewWithJavascriptInterfaces extends WebView {
342        public WebViewWithJavascriptInterfaces(
343                Context context, Map<String, Object> javascriptInterfaces) {
344            super(context,
345                  null, // attribute set
346                  0, // default style resource ID
347                  javascriptInterfaces,
348                  false); // is private browsing
349        }
350    }
351    private WebView createWebViewWithJavascriptInterfaces() {
352        Map<String, Object> javascriptInterfaces = new HashMap<String, Object>();
353        javascriptInterfaces.put("layoutTestController", mLayoutTestController);
354        javascriptInterfaces.put("eventSender", mEventSender);
355        return new WebViewWithJavascriptInterfaces(this, javascriptInterfaces);
356    }
357
358    private void setupWebView(WebView webView) {
359        webView.setWebViewClient(mWebViewClient);
360        webView.setWebChromeClient(mWebChromeClient);
361
362        /**
363         * Setting a touch interval of -1 effectively disables the optimisation in WebView
364         * that stops repeated touch events flooding WebCore. The Event Sender only sends a
365         * single event rather than a stream of events (like what would generally happen in
366         * a real use of touch events in a WebView)  and so if the WebView drops the event,
367         * the test will fail as the test expects one callback for every touch it synthesizes.
368         */
369        webView.setTouchInterval(-1);
370
371        webView.clearCache(true);
372
373        WebSettings webViewSettings = webView.getSettings();
374        webViewSettings.setAppCacheEnabled(true);
375        webViewSettings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
376        webViewSettings.setAppCacheMaxSize(Long.MAX_VALUE);
377        webViewSettings.setJavaScriptEnabled(true);
378        webViewSettings.setJavaScriptCanOpenWindowsAutomatically(true);
379        webViewSettings.setSupportMultipleWindows(true);
380        webViewSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
381        webViewSettings.setDatabaseEnabled(true);
382        webViewSettings.setDatabasePath(getDir("databases", 0).getAbsolutePath());
383        webViewSettings.setDomStorageEnabled(true);
384        webViewSettings.setWorkersEnabled(false);
385        webViewSettings.setXSSAuditorEnabled(false);
386
387        // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
388        mCurrentWebView.useMockDeviceOrientation();
389    }
390
391    private void startTests() {
392        try {
393            Message serviceMsg =
394                    Message.obtain(null, ManagerService.MSG_FIRST_TEST);
395
396            Bundle bundle = new Bundle();
397            if (!mTestsList.isEmpty()) {
398                bundle.putString("firstTest", mTestsList.get(0));
399                bundle.putInt("index", mCurrentTestIndex);
400            }
401
402            serviceMsg.setData(bundle);
403            mManagerServiceMessenger.send(serviceMsg);
404        } catch (RemoteException e) {
405            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
406        }
407
408        runNextTest();
409    }
410
411    private void runNextTest() {
412        assert mCurrentState == CurrentState.IDLE : "mCurrentState = " + mCurrentState.name();
413
414        if (mTestsList.isEmpty()) {
415            onAllTestsFinished();
416            return;
417        }
418
419        mCurrentTestRelativePath = mTestsList.remove(0);
420
421        Log.i(LOG_TAG, "runNextTest(): Start: " + mCurrentTestRelativePath +
422                " (" + mCurrentTestIndex + ")");
423
424        mCurrentTestUri = FileFilter.getUrl(mCurrentTestRelativePath).toString();
425
426        reset();
427
428        /** Start time-out countdown and the test */
429        mCurrentState = CurrentState.RENDERING_PAGE;
430        mResultHandler.sendEmptyMessageDelayed(MSG_TEST_TIMED_OUT, DEFAULT_TIME_OUT_MS);
431        mCurrentWebView.loadUrl(mCurrentTestUri);
432    }
433
434    private void onTestTimedOut() {
435        assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
436
437        Log.w(LOG_TAG, "onTestTimedOut(): " + mCurrentTestRelativePath);
438        mCurrentTestTimedOut = true;
439
440        /**
441         * While it is theoretically possible that the test times out because
442         * of webview becoming unresponsive, it is very unlikely. Therefore it's
443         * assumed that obtaining results (that calls various webview methods)
444         * will not itself hang.
445         */
446        obtainActualResultsFromWebView();
447    }
448
449    private void onTestFinished() {
450        assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
451
452        Log.i(LOG_TAG, "onTestFinished(): " + mCurrentTestRelativePath);
453        mResultHandler.removeMessages(MSG_TEST_TIMED_OUT);
454        obtainActualResultsFromWebView();
455    }
456
457    private void obtainActualResultsFromWebView() {
458        /**
459         * If the result has not been set by the time the test finishes we create
460         * a default type of result.
461         */
462        if (mCurrentResult == null) {
463            /** TODO: Default type should be RenderTreeResult. We don't support it now. */
464            mCurrentResult = new TextResult(mCurrentTestRelativePath);
465        }
466
467        mCurrentState = CurrentState.OBTAINING_RESULT;
468
469        if (mCurrentTestTimedOut) {
470            mCurrentResult.setDidTimeOut();
471        }
472        mCurrentResult.obtainActualResults(mCurrentWebView,
473                mResultHandler.obtainMessage(MSG_ACTUAL_RESULT_OBTAINED));
474    }
475
476    private void onActualResultsObtained() {
477        assert mCurrentState == CurrentState.OBTAINING_RESULT
478                : "mCurrentState = " + mCurrentState.name();
479
480        Log.i(LOG_TAG, "onActualResultsObtained(): " + mCurrentTestRelativePath);
481        mCurrentState = CurrentState.IDLE;
482
483        reportResultToService();
484        mCurrentTestIndex++;
485        updateProgressBar();
486        runNextTest();
487    }
488
489    private void reportResultToService() {
490        if (mCurrentAdditionalTextOutput != null) {
491            mCurrentResult.setAdditionalTextOutputString(mCurrentAdditionalTextOutput.toString());
492        }
493
494        try {
495            Message serviceMsg =
496                    Message.obtain(null, ManagerService.MSG_PROCESS_ACTUAL_RESULTS);
497
498            Bundle bundle = mCurrentResult.getBundle();
499            bundle.putInt("testIndex", mCurrentTestIndex);
500            if (!mTestsList.isEmpty()) {
501                bundle.putString("nextTest", mTestsList.get(0));
502            }
503
504            serviceMsg.setData(bundle);
505            mManagerServiceMessenger.send(serviceMsg);
506        } catch (RemoteException e) {
507            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
508        }
509    }
510
511    private void updateProgressBar() {
512        getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
513                mCurrentTestIndex * Window.PROGRESS_END / mTotalTestCount);
514        setTitle(mCurrentTestIndex * 100 / mTotalTestCount + "% " +
515                "(" + mCurrentTestIndex + "/" + mTotalTestCount + ")");
516    }
517
518    private void onAllTestsFinished() {
519        mScreenDimLock.release();
520
521        try {
522            Message serviceMsg =
523                    Message.obtain(null, ManagerService.MSG_ALL_TESTS_FINISHED);
524            mManagerServiceMessenger.send(serviceMsg);
525        } catch (RemoteException e) {
526            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
527        }
528
529        unbindService(mServiceConnection);
530    }
531
532    private AdditionalTextOutput getCurrentAdditionalTextOutput() {
533        if (mCurrentAdditionalTextOutput == null) {
534            mCurrentAdditionalTextOutput = new AdditionalTextOutput();
535        }
536        return mCurrentAdditionalTextOutput;
537    }
538
539    /** LAYOUT TEST CONTROLLER */
540
541    private static final int MSG_WAIT_UNTIL_DONE = 0;
542    private static final int MSG_NOTIFY_DONE = 1;
543    private static final int MSG_DUMP_AS_TEXT = 2;
544    private static final int MSG_DUMP_CHILD_FRAMES_AS_TEXT = 3;
545    private static final int MSG_SET_CAN_OPEN_WINDOWS = 4;
546    private static final int MSG_DUMP_DATABASE_CALLBACKS = 5;
547    private static final int MSG_SET_GEOLOCATION_PERMISSION = 6;
548    private static final int MSG_OVERRIDE_PREFERENCE = 7;
549    private static final int MSG_SET_XSS_AUDITOR_ENABLED = 8;
550
551    /** String constants for use with layoutTestController.overridePreference() */
552    private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED =
553            "WebKitOfflineWebApplicationCacheEnabled";
554
555    Handler mLayoutTestControllerHandler = new Handler() {
556        @Override
557        public void handleMessage(Message msg) {
558            assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
559
560            switch (msg.what) {
561                case MSG_DUMP_AS_TEXT:
562                    if (mCurrentResult == null) {
563                        mCurrentResult = new TextResult(mCurrentTestRelativePath);
564                    }
565                    assert mCurrentResult instanceof TextResult
566                            : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
567                    break;
568
569                case MSG_DUMP_CHILD_FRAMES_AS_TEXT:
570                    /** If dumpAsText was not called we assume that the result should be text */
571                    if (mCurrentResult == null) {
572                        mCurrentResult = new TextResult(mCurrentTestRelativePath);
573                    }
574
575                    assert mCurrentResult instanceof TextResult
576                            : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
577
578                    ((TextResult)mCurrentResult).setDumpChildFramesAsText(true);
579                    break;
580
581                case MSG_DUMP_DATABASE_CALLBACKS:
582                    mDumpDatabaseCallbacks = true;
583                    break;
584
585                case MSG_NOTIFY_DONE:
586                    if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
587                        onTestFinished();
588                    }
589                    break;
590
591                case MSG_OVERRIDE_PREFERENCE:
592                    /**
593                     * TODO: We should look up the correct WebView for the frame which
594                     * called the layoutTestController method. Currently, we just use the
595                     * WebView for the main frame. EventSender suffers from the same
596                     * problem.
597                     */
598                    if (msg.getData().getString("key").equals(
599                            WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED)) {
600                        mCurrentWebView.getSettings().setAppCacheEnabled(msg.getData().getBoolean(
601                                "value"));
602                    } else {
603                        Log.w(LOG_TAG, "MSG_OVERRIDE_PREFERENCE: unsupported preference!");
604                    }
605                    break;
606
607                case MSG_SET_CAN_OPEN_WINDOWS:
608                    mCanOpenWindows = true;
609                    break;
610
611                case MSG_SET_GEOLOCATION_PERMISSION:
612                    mIsGeolocationPermissionSet = true;
613                    mGeolocationPermission = msg.arg1 == 1;
614
615                    if (mPendingGeolocationPermissionCallbacks != null) {
616                        Iterator<GeolocationPermissions.Callback> iter =
617                                mPendingGeolocationPermissionCallbacks.keySet().iterator();
618                        while (iter.hasNext()) {
619                            GeolocationPermissions.Callback callback = iter.next();
620                            String origin = mPendingGeolocationPermissionCallbacks.get(callback);
621                            callback.invoke(origin, mGeolocationPermission, false);
622                        }
623                        mPendingGeolocationPermissionCallbacks = null;
624                    }
625                    break;
626
627                case MSG_SET_XSS_AUDITOR_ENABLED:
628                    mCurrentWebView.getSettings().setXSSAuditorEnabled(msg.arg1 == 1);
629                    break;
630
631                case MSG_WAIT_UNTIL_DONE:
632                    mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
633                    break;
634
635                default:
636                    assert false : "msg.what=" + msg.what;
637                    break;
638            }
639        }
640    };
641
642    private void resetLayoutTestController() {
643        mCanOpenWindows = false;
644        mDumpDatabaseCallbacks = false;
645        mIsGeolocationPermissionSet = false;
646        mPendingGeolocationPermissionCallbacks = null;
647    }
648
649    public void dumpAsText(boolean enablePixelTest) {
650        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpAsText(" + enablePixelTest + ") called");
651        /** TODO: Implement */
652        if (enablePixelTest) {
653            Log.w(LOG_TAG, "enablePixelTest not implemented, switching to false");
654        }
655        mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_AS_TEXT);
656    }
657
658    public void dumpChildFramesAsText() {
659        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpChildFramesAsText() called");
660        mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
661    }
662
663    public void dumpDatabaseCallbacks() {
664        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
665        mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
666    }
667
668    public void notifyDone() {
669        Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
670        mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
671    }
672
673    public void overridePreference(String key, boolean value) {
674        Log.i(LOG_TAG, mCurrentTestRelativePath + ": overridePreference(" + key + ", " + value +
675        ") called");
676        Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_OVERRIDE_PREFERENCE);
677        msg.getData().putString("key", key);
678        msg.getData().putBoolean("value", value);
679        msg.sendToTarget();
680    }
681
682    public void setCanOpenWindows() {
683        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
684        mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
685    }
686
687    public void setGeolocationPermission(boolean allow) {
688        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
689                ") called");
690        Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
691        msg.arg1 = allow ? 1 : 0;
692        msg.sendToTarget();
693    }
694
695    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
696            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
697        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
698                ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
699                ", " + gamma + ")");
700        mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
701                canProvideGamma, gamma);
702    }
703
704    public void setXSSAuditorEnabled(boolean flag) {
705        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setXSSAuditorEnabled(" + flag + ") called");
706        Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_XSS_AUDITOR_ENABLED);
707        msg.arg1 = flag ? 1 : 0;
708        msg.sendToTarget();
709    }
710
711    public void waitUntilDone() {
712        Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
713        mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
714    }
715}
716