• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RemoteDesktopBrowserTest()25 RemoteDesktopBrowserTest::RemoteDesktopBrowserTest()
26     : extension_(NULL) {
27 }
28 
~RemoteDesktopBrowserTest()29 RemoteDesktopBrowserTest::~RemoteDesktopBrowserTest() {}
30 
SetUp()31 void RemoteDesktopBrowserTest::SetUp() {
32   ParseCommandLine();
33   PlatformAppBrowserTest::SetUp();
34 }
35 
SetUpOnMainThread()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.
SetUpInProcessBrowserTestFixture()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 
TearDownInProcessBrowserTestFixture()54 void RemoteDesktopBrowserTest::TearDownInProcessBrowserTestFixture() {
55   DisableDNSLookupForThisTest();
56 }
57 
VerifyInternetAccess()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 
HtmlElementVisible(const std::string & name)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 
InstallChromotingAppCrx()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 
InstallChromotingAppUnpacked()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 
UninstallChromotingApp()113 void RemoteDesktopBrowserTest::UninstallChromotingApp() {
114   UninstallExtension(ChromotingID());
115   extension_ = NULL;
116 }
117 
VerifyChromotingLoaded(bool expected)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 
LaunchChromotingApp()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 
Authorize()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 
Authenticate()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 
Approve()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 
ExpandMe2Me()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 
DisconnectMe2Me()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 
SimulateKeyPressWithCode(ui::KeyboardCode keyCode,const char * code)364 void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
365     ui::KeyboardCode keyCode,
366     const char* code) {
367   SimulateKeyPressWithCode(keyCode, code, false, false, false, false);
368 }
369 
SimulateKeyPressWithCode(ui::KeyboardCode keyCode,const char * code,bool control,bool shift,bool alt,bool command)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 
SimulateCharInput(char c)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 
SimulateStringInput(const std::string & input)396 void RemoteDesktopBrowserTest::SimulateStringInput(const std::string& input) {
397   for (size_t i = 0; i < input.length(); ++i)
398     SimulateCharInput(input[i]);
399 }
400 
SimulateMouseLeftClickAt(int x,int y)401 void RemoteDesktopBrowserTest::SimulateMouseLeftClickAt(int x, int y) {
402   SimulateMouseClickAt(0, blink::WebMouseEvent::ButtonLeft, x, y);
403 }
404 
SimulateMouseClickAt(int modifiers,blink::WebMouseEvent::Button button,int x,int y)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 
Install()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 
Cleanup()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 
Auth()456 void RemoteDesktopBrowserTest::Auth() {
457   Authorize();
458   Authenticate();
459   Approve();
460 }
461 
ConnectToLocalHost(bool remember_pin)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 
ConnectToRemoteHost(const std::string & host_name,bool remember_pin)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 
EnableDNSLookupForThisTest(net::RuleBasedHostResolverProc * host_resolver)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 
DisableDNSLookupForThisTest()517 void RemoteDesktopBrowserTest::DisableDNSLookupForThisTest() {
518   mock_host_resolver_override_.reset();
519 }
520 
ParseCommandLine()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 
ExecuteScript(const std::string & script)557 void RemoteDesktopBrowserTest::ExecuteScript(const std::string& script) {
558   ASSERT_TRUE(content::ExecuteScript(active_web_contents(), script));
559 }
560 
ExecuteScriptAndWaitForAnyPageLoad(const std::string & script)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
ExecuteScriptAndExtractBool(content::WebContents * web_contents,const std::string & script)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
ExecuteScriptAndExtractInt(content::WebContents * web_contents,const std::string & script)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
ExecuteScriptAndExtractString(content::WebContents * web_contents,const std::string & script)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 
ClickOnControl(const std::string & name)610 void RemoteDesktopBrowserTest::ClickOnControl(const std::string& name) {
611   ASSERT_TRUE(HtmlElementVisible(name));
612 
613   ExecuteScript("document.getElementById(\"" + name + "\").click();");
614 }
615 
EnterPin(const std::string & pin,bool remember_pin)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 
WaitForConnection()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 
IsLocalHostReady()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 
IsSessionConnected()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 
IsPinFormVisible()680 bool RemoteDesktopBrowserTest::IsPinFormVisible() {
681   DismissHostVersionWarningIfVisible();
682   return HtmlElementVisible("pin-form");
683 }
684 
DismissHostVersionWarningIfVisible()685 void RemoteDesktopBrowserTest::DismissHostVersionWarningIfVisible() {
686   if (HtmlElementVisible("host-needs-update-connect-button"))
687     ClickOnControl("host-needs-update-connect-button");
688 }
689 
690 // static
IsAuthenticatedInWindow(content::WebContents * web_contents)691 bool RemoteDesktopBrowserTest::IsAuthenticatedInWindow(
692     content::WebContents* web_contents) {
693   return ExecuteScriptAndExtractBool(
694       web_contents, "remoting.identity.isAuthenticated()");
695 }
696 
697 }  // namespace remoting
698