• 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.WebStorage;
45 import android.webkit.WebStorage.QuotaUpdater;
46 import android.webkit.WebView;
47 import android.webkit.WebViewClient;
48 
49 import java.lang.Thread.UncaughtExceptionHandler;
50 import java.util.HashMap;
51 import java.util.Iterator;
52 import java.util.List;
53 import 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  */
61 public class LayoutTestsExecutor extends Activity {
62 
63     private enum CurrentState {
64         IDLE,
65         RENDERING_PAGE,
66         WAITING_FOR_ASYNCHRONOUS_TEST,
67         OBTAINING_RESULT;
68 
isRunningState()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
onCreate(Bundle savedInstanceState)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 
reset()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 {
WebViewWithJavascriptInterfaces( Context context, Map<String, Object> javascriptInterfaces)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     }
createWebViewWithJavascriptInterfaces()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 
setupWebView(WebView webView)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 
startTests()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 
sendResetMessage()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 
sendFirstTestMessage()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 
runNextTest()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 
onTestTimedOut()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 
onTestFinished()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 
obtainActualResultsFromWebView()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 
onActualResultsObtained()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 
reportResultToService()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 
updateProgressBar()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 
onAllTestsFinished()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 
getCurrentAdditionalTextOutput()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 
resetLayoutTestController()673     private void resetLayoutTestController() {
674         mCanOpenWindows = false;
675         mDumpDatabaseCallbacks = false;
676         mIsGeolocationPermissionSet = false;
677         mPendingGeolocationPermissionCallbacks = null;
678     }
679 
dumpAsText(boolean enablePixelTest)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 
dumpChildFramesAsText()689     public void dumpChildFramesAsText() {
690         Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpChildFramesAsText() called");
691         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
692     }
693 
dumpDatabaseCallbacks()694     public void dumpDatabaseCallbacks() {
695         Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
696         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
697     }
698 
notifyDone()699     public void notifyDone() {
700         Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
701         mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
702     }
703 
overridePreference(String key, boolean value)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 
setCanOpenWindows()713     public void setCanOpenWindows() {
714         Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
715         mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
716     }
717 
setGeolocationPermission(boolean allow)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 
setMockDeviceOrientation(boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma)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 
setXSSAuditorEnabled(boolean flag)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 
waitUntilDone()742     public void waitUntilDone() {
743         Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
744         mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
745     }
746 
747 }
748