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