Home | History | Annotate | Download | only in remoting
      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