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