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