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