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