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