1# Copyright (c) 2012 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.
4import logging
5
6from telemetry.core import util
7
8
9def _WaitForLoginFormToLoad(backend, login_form_id, tab):
10  def IsFormLoadedOrAlreadyLoggedIn():
11    return tab.EvaluateJavaScript(
12        'document.querySelector("#%s")!== null' % login_form_id) or \
13            backend.IsAlreadyLoggedIn(tab)
14
15  # Wait until the form is submitted and the page completes loading.
16  util.WaitFor(lambda: IsFormLoadedOrAlreadyLoggedIn(), # pylint: disable=W0108
17               60)
18
19def _SubmitFormAndWait(form_id, tab):
20  js = 'document.getElementById("%s").submit();' % form_id
21  tab.ExecuteJavaScript(js)
22
23  def IsLoginStillHappening():
24    return tab.EvaluateJavaScript(
25        'document.querySelector("#%s")!== null' % form_id)
26
27  # Wait until the form is submitted and the page completes loading.
28  util.WaitFor(lambda: not IsLoginStillHappening(), 60)
29
30class FormBasedCredentialsBackend(object):
31  def __init__(self):
32    self._logged_in = False
33
34  def IsAlreadyLoggedIn(self, tab):
35    raise NotImplementedError()
36
37  @property
38  def credentials_type(self):
39    raise NotImplementedError()
40
41  @property
42  def url(self):
43    raise NotImplementedError()
44
45  @property
46  def login_form_id(self):
47    raise NotImplementedError()
48
49  @property
50  def login_input_id(self):
51    raise NotImplementedError()
52
53  @property
54  def password_input_id(self):
55    raise NotImplementedError()
56
57  def IsLoggedIn(self):
58    return self._logged_in
59
60  def _ResetLoggedInState(self):
61    """Makes the backend think we're not logged in even though we are.
62    Should only be used in unit tests to simulate --dont-override-profile.
63    """
64    self._logged_in = False
65
66  def LoginNeeded(self, tab, config):
67    """Logs in to a test account.
68
69    Raises:
70      RuntimeError: if could not get credential information.
71    """
72    if self._logged_in:
73      return True
74
75    if 'username' not in config or 'password' not in config:
76      message = ('Credentials for "%s" must include username and password.' %
77                 self.credentials_type)
78      raise RuntimeError(message)
79
80    logging.debug('Logging into %s account...' % self.credentials_type)
81
82    try:
83      logging.info('Loading %s...', self.url)
84      tab.Navigate(self.url)
85      _WaitForLoginFormToLoad(self, self.login_form_id, tab)
86
87      if self.IsAlreadyLoggedIn(tab):
88        self._logged_in = True
89        return True
90
91      tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
92      logging.info('Loaded page: %s', self.url)
93
94      email_id = 'document.querySelector("#%s").%s.value = "%s"; ' % (
95          self.login_form_id, self.login_input_id, config['username'])
96      password = 'document.querySelector("#%s").%s.value = "%s"; ' % (
97          self.login_form_id, self.password_input_id, config['password'])
98      tab.ExecuteJavaScript(email_id)
99      tab.ExecuteJavaScript(password)
100
101      _SubmitFormAndWait(self.login_form_id, tab)
102
103      self._logged_in = True
104      return True
105    except util.TimeoutException:
106      logging.warning('Timed out while loading: %s', self.url)
107      return False
108
109  def LoginNoLongerNeeded(self, tab): # pylint: disable=W0613
110    assert self._logged_in
111