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 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