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