1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/test/remoting/remote_desktop_browsertest.h" 6 7 #include "base/command_line.h" 8 #include "chrome/browser/extensions/extension_service.h" 9 #include "chrome/browser/extensions/unpacked_installer.h" 10 #include "chrome/browser/ui/extensions/application_launch.h" 11 #include "chrome/common/chrome_switches.h" 12 #include "chrome/common/extensions/extension_file_util.h" 13 #include "chrome/test/remoting/key_code_conv.h" 14 #include "chrome/test/remoting/page_load_notification_observer.h" 15 #include "chrome/test/remoting/waiter.h" 16 #include "content/public/browser/native_web_keyboard_event.h" 17 #include "content/public/browser/render_view_host.h" 18 #include "content/public/test/test_utils.h" 19 #include "extensions/common/constants.h" 20 #include "extensions/common/extension.h" 21 #include "ui/base/window_open_disposition.h" 22 23 namespace remoting { 24 25 RemoteDesktopBrowserTest::RemoteDesktopBrowserTest() 26 : extension_(NULL) { 27 } 28 29 RemoteDesktopBrowserTest::~RemoteDesktopBrowserTest() {} 30 31 void RemoteDesktopBrowserTest::SetUp() { 32 ParseCommandLine(); 33 PlatformAppBrowserTest::SetUp(); 34 } 35 36 void RemoteDesktopBrowserTest::SetUpOnMainThread() { 37 PlatformAppBrowserTest::SetUpOnMainThread(); 38 39 // Pushing the initial WebContents instance onto the stack before 40 // RunTestOnMainThread() and after |InProcessBrowserTest::browser_| 41 // is initialized in InProcessBrowserTest::RunTestOnMainThreadLoop() 42 web_contents_stack_.push_back( 43 browser()->tab_strip_model()->GetActiveWebContents()); 44 } 45 46 // Change behavior of the default host resolver to avoid DNS lookup errors, 47 // so we can make network calls. 48 void RemoteDesktopBrowserTest::SetUpInProcessBrowserTestFixture() { 49 // The resolver object lifetime is managed by sync_test_setup, not here. 50 EnableDNSLookupForThisTest( 51 new net::RuleBasedHostResolverProc(host_resolver())); 52 } 53 54 void RemoteDesktopBrowserTest::TearDownInProcessBrowserTestFixture() { 55 DisableDNSLookupForThisTest(); 56 } 57 58 void RemoteDesktopBrowserTest::VerifyInternetAccess() { 59 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( 60 browser(), GURL("http://www.google.com"), 1); 61 62 EXPECT_EQ(GetCurrentURL().host(), "www.google.com"); 63 } 64 65 bool RemoteDesktopBrowserTest::HtmlElementVisible(const std::string& name) { 66 _ASSERT_TRUE(HtmlElementExists(name)); 67 68 ExecuteScript( 69 "function isElementVisible(name) {" 70 " var element = document.getElementById(name);" 71 " /* The existence of the element has already been ASSERTed. */" 72 " do {" 73 " if (element.hidden) {" 74 " return false;" 75 " }" 76 " element = element.parentNode;" 77 " } while (element != null);" 78 " return true;" 79 "};"); 80 81 return ExecuteScriptAndExtractBool( 82 "isElementVisible(\"" + name + "\")"); 83 } 84 85 void RemoteDesktopBrowserTest::InstallChromotingAppCrx() { 86 ASSERT_TRUE(!is_unpacked()); 87 88 base::FilePath install_dir(WebAppCrxPath()); 89 scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm( 90 install_dir, 1, browser())); 91 92 EXPECT_FALSE(extension.get() == NULL); 93 94 extension_ = extension.get(); 95 } 96 97 void RemoteDesktopBrowserTest::InstallChromotingAppUnpacked() { 98 ASSERT_TRUE(is_unpacked()); 99 100 scoped_refptr<extensions::UnpackedInstaller> installer = 101 extensions::UnpackedInstaller::Create(extension_service()); 102 installer->set_prompt_for_plugins(false); 103 104 content::WindowedNotificationObserver observer( 105 chrome::NOTIFICATION_EXTENSION_LOADED, 106 content::NotificationService::AllSources()); 107 108 installer->Load(webapp_unpacked_); 109 110 observer.Wait(); 111 } 112 113 void RemoteDesktopBrowserTest::UninstallChromotingApp() { 114 UninstallExtension(ChromotingID()); 115 extension_ = NULL; 116 } 117 118 void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) { 119 const ExtensionSet* extensions = extension_service()->extensions(); 120 scoped_refptr<const extensions::Extension> extension; 121 ExtensionSet::const_iterator iter; 122 bool installed = false; 123 124 for (iter = extensions->begin(); iter != extensions->end(); ++iter) { 125 extension = *iter; 126 // Is there a better way to recognize the chromoting extension 127 // than name comparison? 128 if (extension->name() == "Chromoting" || 129 extension->name() == "Chrome Remote Desktop") { 130 installed = true; 131 break; 132 } 133 } 134 135 if (installed) { 136 if (extension_) 137 EXPECT_EQ(extension, extension_); 138 else 139 extension_ = extension.get(); 140 141 // Either a V1 (TYPE_LEGACY_PACKAGED_APP) or a V2 (TYPE_PLATFORM_APP ) app. 142 extensions::Manifest::Type type = extension->GetType(); 143 EXPECT_TRUE(type == extensions::Manifest::TYPE_PLATFORM_APP || 144 type == extensions::Manifest::TYPE_LEGACY_PACKAGED_APP); 145 146 EXPECT_TRUE(extension->ShouldDisplayInAppLauncher()); 147 } 148 149 EXPECT_EQ(installed, expected); 150 } 151 152 void RemoteDesktopBrowserTest::LaunchChromotingApp() { 153 ASSERT_TRUE(extension_); 154 155 GURL chromoting_main = Chromoting_Main_URL(); 156 // We cannot simply wait for any page load because the first page 157 // loaded could be the generated background page. We need to wait 158 // till the chromoting main page is loaded. 159 PageLoadNotificationObserver observer(chromoting_main); 160 161 OpenApplication(AppLaunchParams( 162 browser()->profile(), 163 extension_, 164 is_platform_app() ? extensions::LAUNCH_CONTAINER_NONE : 165 extensions::LAUNCH_CONTAINER_TAB, 166 is_platform_app() ? NEW_WINDOW : CURRENT_TAB)); 167 168 observer.Wait(); 169 170 171 // The active WebContents instance should be the source of the LOAD_STOP 172 // notification. 173 content::NavigationController* controller = 174 content::Source<content::NavigationController>(observer.source()).ptr(); 175 176 content::WebContents* web_contents = controller->GetWebContents(); 177 if (web_contents != active_web_contents()) 178 web_contents_stack_.push_back(web_contents); 179 180 if (is_platform_app()) { 181 EXPECT_EQ(GetFirstShellWindowWebContents(), active_web_contents()); 182 } else { 183 // For apps v1 only, the DOMOperationObserver is not ready at the LOAD_STOP 184 // event. A half second wait is necessary for the subsequent javascript 185 // injection to work. 186 // TODO(weitaosu): Find out whether there is a more appropriate notification 187 // to wait for so we can get rid of this wait. 188 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromMilliseconds(500)).Wait()); 189 } 190 191 EXPECT_EQ(Chromoting_Main_URL(), GetCurrentURL()); 192 } 193 194 void RemoteDesktopBrowserTest::Authorize() { 195 // The chromoting extension should be installed. 196 ASSERT_TRUE(extension_); 197 198 // The chromoting main page should be loaded in the current tab 199 // and isAuthenticated() should be false (auth dialog visible). 200 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL()); 201 ASSERT_FALSE(IsAuthenticated()); 202 203 // The second observer monitors the loading of the Google login page. 204 // Unfortunately we cannot specify a source in this observer because 205 // we can't get a handle of the new window until the first observer 206 // has finished waiting. But we will assert that the source of the 207 // load stop event is indeed the newly created browser window. 208 content::WindowedNotificationObserver observer( 209 content::NOTIFICATION_LOAD_STOP, 210 content::NotificationService::AllSources()); 211 212 ClickOnControl("auth-button"); 213 214 observer.Wait(); 215 216 content::NavigationController* controller = 217 content::Source<content::NavigationController>(observer.source()).ptr(); 218 219 web_contents_stack_.push_back(controller->GetWebContents()); 220 221 // Verify the active tab is at the "Google Accounts" login page. 222 EXPECT_EQ("accounts.google.com", GetCurrentURL().host()); 223 EXPECT_TRUE(HtmlElementExists("Email")); 224 EXPECT_TRUE(HtmlElementExists("Passwd")); 225 } 226 227 void RemoteDesktopBrowserTest::Authenticate() { 228 // The chromoting extension should be installed. 229 ASSERT_TRUE(extension_); 230 231 // The active tab should have the "Google Accounts" login page loaded. 232 ASSERT_EQ("accounts.google.com", GetCurrentURL().host()); 233 ASSERT_TRUE(HtmlElementExists("Email")); 234 ASSERT_TRUE(HtmlElementExists("Passwd")); 235 236 // Now log in using the username and password passed in from the command line. 237 ExecuteScriptAndWaitForAnyPageLoad( 238 "document.getElementById(\"Email\").value = \"" + username_ + "\";" + 239 "document.getElementById(\"Passwd\").value = \"" + password_ +"\";" + 240 "document.forms[\"gaia_loginform\"].submit();"); 241 242 // TODO(weitaosu): Is there a better way to verify we are on the 243 // "Request for Permission" page? 244 // V2 app won't ask for approval here because the chromoting test account 245 // has already been granted permissions. 246 if (!is_platform_app()) { 247 EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com"); 248 EXPECT_TRUE(HtmlElementExists("submit_approve_access")); 249 } 250 } 251 252 void RemoteDesktopBrowserTest::Approve() { 253 // The chromoting extension should be installed. 254 ASSERT_TRUE(extension_); 255 256 if (is_platform_app()) { 257 // Popping the login window off the stack to return to the chromoting 258 // window. 259 web_contents_stack_.pop_back(); 260 261 // There is nothing for the V2 app to approve because the chromoting test 262 // account has already been granted permissions. 263 // TODO(weitaosu): Revoke the permission at the beginning of the test so 264 // that we can test first-time experience here. 265 ConditionalTimeoutWaiter waiter( 266 base::TimeDelta::FromSeconds(3), 267 base::TimeDelta::FromSeconds(1), 268 base::Bind( 269 &RemoteDesktopBrowserTest::IsAuthenticatedInWindow, 270 active_web_contents())); 271 272 ASSERT_TRUE(waiter.Wait()); 273 } else { 274 ASSERT_EQ("accounts.google.com", GetCurrentURL().host()); 275 276 // Is there a better way to verify we are on the "Request for Permission" 277 // page? 278 ASSERT_TRUE(HtmlElementExists("submit_approve_access")); 279 280 const GURL chromoting_main = Chromoting_Main_URL(); 281 282 // active_web_contents() is still the login window but the observer 283 // should be set up to observe the chromoting window because that is 284 // where we'll receive the message from the login window and reload the 285 // chromoting app. 286 content::WindowedNotificationObserver observer( 287 content::NOTIFICATION_LOAD_STOP, 288 base::Bind( 289 &RemoteDesktopBrowserTest::IsAuthenticatedInWindow, 290 browser()->tab_strip_model()->GetActiveWebContents())); 291 292 ExecuteScript( 293 "lso.approveButtonAction();" 294 "document.forms[\"connect-approve\"].submit();"); 295 296 observer.Wait(); 297 298 // Popping the login window off the stack to return to the chromoting 299 // window. 300 web_contents_stack_.pop_back(); 301 } 302 303 ASSERT_TRUE(GetCurrentURL() == Chromoting_Main_URL()); 304 305 EXPECT_TRUE(IsAuthenticated()); 306 } 307 308 void RemoteDesktopBrowserTest::ExpandMe2Me() { 309 // The chromoting extension should be installed. 310 ASSERT_TRUE(extension_); 311 312 // The active tab should have the chromoting app loaded. 313 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL()); 314 EXPECT_TRUE(IsAuthenticated()); 315 316 // The Me2Me host list should be hidden. 317 ASSERT_FALSE(HtmlElementVisible("me2me-content")); 318 // The Me2Me "Get Start" button should be visible. 319 ASSERT_TRUE(HtmlElementVisible("get-started-me2me")); 320 321 // Starting Me2Me. 322 ExecuteScript("remoting.showMe2MeUiAndSave();"); 323 324 EXPECT_TRUE(HtmlElementVisible("me2me-content")); 325 EXPECT_FALSE(HtmlElementVisible("me2me-first-run")); 326 327 // Wait until localHost is initialized. This can take a while. 328 ConditionalTimeoutWaiter waiter( 329 base::TimeDelta::FromSeconds(3), 330 base::TimeDelta::FromSeconds(1), 331 base::Bind(&RemoteDesktopBrowserTest::IsLocalHostReady, this)); 332 EXPECT_TRUE(waiter.Wait()); 333 334 EXPECT_TRUE(ExecuteScriptAndExtractBool( 335 "remoting.hostList.localHost_.hostName && " 336 "remoting.hostList.localHost_.hostId && " 337 "remoting.hostList.localHost_.status && " 338 "remoting.hostList.localHost_.status == 'ONLINE'")); 339 } 340 341 void RemoteDesktopBrowserTest::DisconnectMe2Me() { 342 // The chromoting extension should be installed. 343 ASSERT_TRUE(extension_); 344 345 // The active tab should have the chromoting app loaded. 346 ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL()); 347 ASSERT_TRUE(RemoteDesktopBrowserTest::IsSessionConnected()); 348 349 ClickOnControl("toolbar-stub"); 350 351 EXPECT_TRUE(HtmlElementVisible("session-toolbar")); 352 353 ClickOnControl("toolbar-disconnect"); 354 355 EXPECT_TRUE(HtmlElementVisible("client-dialog")); 356 EXPECT_TRUE(HtmlElementVisible("client-reconnect-button")); 357 EXPECT_TRUE(HtmlElementVisible("client-finished-me2me-button")); 358 359 ClickOnControl("client-finished-me2me-button"); 360 361 EXPECT_FALSE(HtmlElementVisible("client-dialog")); 362 } 363 364 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode( 365 ui::KeyboardCode keyCode, 366 const char* code) { 367 SimulateKeyPressWithCode(keyCode, code, false, false, false, false); 368 } 369 370 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode( 371 ui::KeyboardCode keyCode, 372 const char* code, 373 bool control, 374 bool shift, 375 bool alt, 376 bool command) { 377 content::SimulateKeyPressWithCode( 378 active_web_contents(), 379 keyCode, 380 code, 381 control, 382 shift, 383 alt, 384 command); 385 } 386 387 void RemoteDesktopBrowserTest::SimulateCharInput(char c) { 388 const char* code; 389 ui::KeyboardCode keyboard_code; 390 bool shift; 391 GetKeyValuesFromChar(c, &code, &keyboard_code, &shift); 392 ASSERT_TRUE(code != NULL); 393 SimulateKeyPressWithCode(keyboard_code, code, false, shift, false, false); 394 } 395 396 void RemoteDesktopBrowserTest::SimulateStringInput(const std::string& input) { 397 for (size_t i = 0; i < input.length(); ++i) 398 SimulateCharInput(input[i]); 399 } 400 401 void RemoteDesktopBrowserTest::SimulateMouseLeftClickAt(int x, int y) { 402 SimulateMouseClickAt(0, blink::WebMouseEvent::ButtonLeft, x, y); 403 } 404 405 void RemoteDesktopBrowserTest::SimulateMouseClickAt( 406 int modifiers, blink::WebMouseEvent::Button button, int x, int y) { 407 // TODO(weitaosu): The coordinate translation doesn't work correctly for 408 // apps v2. 409 ExecuteScript( 410 "var clientPluginElement = " 411 "document.getElementById('session-client-plugin');" 412 "var clientPluginRect = clientPluginElement.getBoundingClientRect();"); 413 414 int top = ExecuteScriptAndExtractInt("clientPluginRect.top"); 415 int left = ExecuteScriptAndExtractInt("clientPluginRect.left"); 416 int width = ExecuteScriptAndExtractInt("clientPluginRect.width"); 417 int height = ExecuteScriptAndExtractInt("clientPluginRect.height"); 418 419 ASSERT_GT(x, 0); 420 ASSERT_LT(x, width); 421 ASSERT_GT(y, 0); 422 ASSERT_LT(y, height); 423 424 content::SimulateMouseClickAt( 425 browser()->tab_strip_model()->GetActiveWebContents(), 426 modifiers, 427 button, 428 gfx::Point(left + x, top + y)); 429 } 430 431 void RemoteDesktopBrowserTest::Install() { 432 if (!NoInstall()) { 433 VerifyChromotingLoaded(false); 434 if (is_unpacked()) 435 InstallChromotingAppUnpacked(); 436 else 437 InstallChromotingAppCrx(); 438 } 439 440 VerifyChromotingLoaded(true); 441 } 442 443 void RemoteDesktopBrowserTest::Cleanup() { 444 // TODO(weitaosu): Remove this hack by blocking on the appropriate 445 // notification. 446 // The browser may still be loading images embedded in the webapp. If we 447 // uinstall it now those load will fail. 448 ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait()); 449 450 if (!NoCleanup()) { 451 UninstallChromotingApp(); 452 VerifyChromotingLoaded(false); 453 } 454 } 455 456 void RemoteDesktopBrowserTest::Auth() { 457 Authorize(); 458 Authenticate(); 459 Approve(); 460 } 461 462 void RemoteDesktopBrowserTest::ConnectToLocalHost(bool remember_pin) { 463 // Verify that the local host is online. 464 ASSERT_TRUE(ExecuteScriptAndExtractBool( 465 "remoting.hostList.localHost_.hostName && " 466 "remoting.hostList.localHost_.hostId && " 467 "remoting.hostList.localHost_.status && " 468 "remoting.hostList.localHost_.status == 'ONLINE'")); 469 470 // Connect. 471 ClickOnControl("this-host-connect"); 472 473 // Enter the pin # passed in from the command line. 474 EnterPin(me2me_pin(), remember_pin); 475 476 WaitForConnection(); 477 } 478 479 void RemoteDesktopBrowserTest::ConnectToRemoteHost( 480 const std::string& host_name, bool remember_pin) { 481 std::string host_id = ExecuteScriptAndExtractString( 482 "remoting.hostList.getHostIdForName('" + host_name + "')"); 483 484 EXPECT_FALSE(host_id.empty()); 485 std::string element_id = "host_" + host_id; 486 487 // Verify the host is online. 488 std::string host_div_class = ExecuteScriptAndExtractString( 489 "document.getElementById('" + element_id + "').parentNode.className"); 490 EXPECT_NE(std::string::npos, host_div_class.find("host-online")); 491 492 ClickOnControl(element_id); 493 494 // Enter the pin # passed in from the command line. 495 EnterPin(me2me_pin(), remember_pin); 496 497 WaitForConnection(); 498 } 499 500 void RemoteDesktopBrowserTest::EnableDNSLookupForThisTest( 501 net::RuleBasedHostResolverProc* host_resolver) { 502 // mock_host_resolver_override_ takes ownership of the resolver. 503 scoped_refptr<net::RuleBasedHostResolverProc> resolver = 504 new net::RuleBasedHostResolverProc(host_resolver); 505 resolver->AllowDirectLookup("*.google.com"); 506 // On Linux, we use Chromium's NSS implementation which uses the following 507 // hosts for certificate verification. Without these overrides, running the 508 // integration tests on Linux causes errors as we make external DNS lookups. 509 resolver->AllowDirectLookup("*.thawte.com"); 510 resolver->AllowDirectLookup("*.geotrust.com"); 511 resolver->AllowDirectLookup("*.gstatic.com"); 512 resolver->AllowDirectLookup("*.googleapis.com"); 513 mock_host_resolver_override_.reset( 514 new net::ScopedDefaultHostResolverProc(resolver.get())); 515 } 516 517 void RemoteDesktopBrowserTest::DisableDNSLookupForThisTest() { 518 mock_host_resolver_override_.reset(); 519 } 520 521 void RemoteDesktopBrowserTest::ParseCommandLine() { 522 CommandLine* command_line = CommandLine::ForCurrentProcess(); 523 524 // The test framework overrides any command line user-data-dir 525 // argument with a /tmp/.org.chromium.Chromium.XXXXXX directory. 526 // That happens in the ChromeTestLauncherDelegate, and affects 527 // all unit tests (no opt out available). It intentionally erases 528 // any --user-data-dir switch if present and appends a new one. 529 // Re-override the default data dir if override-user-data-dir 530 // is specified. 531 if (command_line->HasSwitch(kOverrideUserDataDir)) { 532 const base::FilePath& override_user_data_dir = 533 command_line->GetSwitchValuePath(kOverrideUserDataDir); 534 535 ASSERT_FALSE(override_user_data_dir.empty()); 536 537 command_line->AppendSwitchPath(switches::kUserDataDir, 538 override_user_data_dir); 539 } 540 541 username_ = command_line->GetSwitchValueASCII(kUsername); 542 password_ = command_line->GetSwitchValueASCII(kkPassword); 543 me2me_pin_ = command_line->GetSwitchValueASCII(kMe2MePin); 544 remote_host_name_ = command_line->GetSwitchValueASCII(kRemoteHostName); 545 546 no_cleanup_ = command_line->HasSwitch(kNoCleanup); 547 no_install_ = command_line->HasSwitch(kNoInstall); 548 549 if (!no_install_) { 550 webapp_crx_ = command_line->GetSwitchValuePath(kWebAppCrx); 551 webapp_unpacked_ = command_line->GetSwitchValuePath(kWebAppUnpacked); 552 // One and only one of these two arguments should be provided. 553 ASSERT_NE(webapp_crx_.empty(), webapp_unpacked_.empty()); 554 } 555 } 556 557 void RemoteDesktopBrowserTest::ExecuteScript(const std::string& script) { 558 ASSERT_TRUE(content::ExecuteScript(active_web_contents(), script)); 559 } 560 561 void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForAnyPageLoad( 562 const std::string& script) { 563 content::WindowedNotificationObserver observer( 564 content::NOTIFICATION_LOAD_STOP, 565 content::Source<content::NavigationController>( 566 &active_web_contents()-> 567 GetController())); 568 569 ExecuteScript(script); 570 571 observer.Wait(); 572 } 573 574 // static 575 bool RemoteDesktopBrowserTest::ExecuteScriptAndExtractBool( 576 content::WebContents* web_contents, const std::string& script) { 577 bool result; 578 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( 579 web_contents, 580 "window.domAutomationController.send(" + script + ");", 581 &result)); 582 583 return result; 584 } 585 586 // static 587 int RemoteDesktopBrowserTest::ExecuteScriptAndExtractInt( 588 content::WebContents* web_contents, const std::string& script) { 589 int result; 590 _ASSERT_TRUE(content::ExecuteScriptAndExtractInt( 591 web_contents, 592 "window.domAutomationController.send(" + script + ");", 593 &result)); 594 595 return result; 596 } 597 598 // static 599 std::string RemoteDesktopBrowserTest::ExecuteScriptAndExtractString( 600 content::WebContents* web_contents, const std::string& script) { 601 std::string result; 602 _ASSERT_TRUE(content::ExecuteScriptAndExtractString( 603 web_contents, 604 "window.domAutomationController.send(" + script + ");", 605 &result)); 606 607 return result; 608 } 609 610 void RemoteDesktopBrowserTest::ClickOnControl(const std::string& name) { 611 ASSERT_TRUE(HtmlElementVisible(name)); 612 613 ExecuteScript("document.getElementById(\"" + name + "\").click();"); 614 } 615 616 void RemoteDesktopBrowserTest::EnterPin(const std::string& pin, 617 bool remember_pin) { 618 // Wait for the pin-form to be displayed. This can take a while. 619 // We also need to dismiss the host-needs-update dialog if it comes up. 620 // TODO(weitaosu) 1: Instead of polling, can we register a callback to be 621 // called when the pin-form is ready? 622 // TODO(weitaosu) 2: Instead of blindly dismiss the host-needs-update dialog, 623 // we should verify that it only pops up at the right circumstance. That 624 // probably belongs in a separate test case though. 625 ConditionalTimeoutWaiter waiter( 626 base::TimeDelta::FromSeconds(5), 627 base::TimeDelta::FromSeconds(1), 628 base::Bind(&RemoteDesktopBrowserTest::IsPinFormVisible, this)); 629 EXPECT_TRUE(waiter.Wait()); 630 631 ExecuteScript( 632 "document.getElementById(\"pin-entry\").value = \"" + pin + "\";"); 633 634 if (remember_pin) { 635 EXPECT_TRUE(HtmlElementVisible("remember-pin")); 636 EXPECT_FALSE(ExecuteScriptAndExtractBool( 637 "document.getElementById('remember-pin-checkbox').checked")); 638 ClickOnControl("remember-pin"); 639 EXPECT_TRUE(ExecuteScriptAndExtractBool( 640 "document.getElementById('remember-pin-checkbox').checked")); 641 } 642 643 ClickOnControl("pin-connect-button"); 644 } 645 646 void RemoteDesktopBrowserTest::WaitForConnection() { 647 // Wait until the client has connected to the server. 648 // This can take a while. 649 // TODO(weitaosu): Instead of polling, can we register a callback to 650 // remoting.clientSession.onStageChange_? 651 ConditionalTimeoutWaiter waiter( 652 base::TimeDelta::FromSeconds(4), 653 base::TimeDelta::FromSeconds(1), 654 base::Bind(&RemoteDesktopBrowserTest::IsSessionConnected, this)); 655 EXPECT_TRUE(waiter.Wait()); 656 657 // The client is not yet ready to take input when the session state becomes 658 // CONNECTED. Wait for 2 seconds for the client to become ready. 659 // TODO(weitaosu): Find a way to detect when the client is truly ready. 660 TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait(); 661 } 662 663 bool RemoteDesktopBrowserTest::IsLocalHostReady() { 664 // TODO(weitaosu): Instead of polling, can we register a callback to 665 // remoting.hostList.setLocalHost_? 666 return ExecuteScriptAndExtractBool("remoting.hostList.localHost_ != null"); 667 } 668 669 bool RemoteDesktopBrowserTest::IsSessionConnected() { 670 // If some form of PINless authentication is enabled, the host version 671 // warning may appear while waiting for the session to connect. 672 DismissHostVersionWarningIfVisible(); 673 674 return ExecuteScriptAndExtractBool( 675 "remoting.clientSession != null && " 676 "remoting.clientSession.getState() == " 677 "remoting.ClientSession.State.CONNECTED"); 678 } 679 680 bool RemoteDesktopBrowserTest::IsPinFormVisible() { 681 DismissHostVersionWarningIfVisible(); 682 return HtmlElementVisible("pin-form"); 683 } 684 685 void RemoteDesktopBrowserTest::DismissHostVersionWarningIfVisible() { 686 if (HtmlElementVisible("host-needs-update-connect-button")) 687 ClickOnControl("host-needs-update-connect-button"); 688 } 689 690 // static 691 bool RemoteDesktopBrowserTest::IsAuthenticatedInWindow( 692 content::WebContents* web_contents) { 693 return ExecuteScriptAndExtractBool( 694 web_contents, "remoting.identity.isAuthenticated()"); 695 } 696 697 } // namespace remoting 698