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