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