1# Copyright 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.
4
5"""Finds browsers that can be controlled by telemetry."""
6
7import logging
8import operator
9
10from telemetry import decorators
11from telemetry.internal.backends.chrome import android_browser_finder
12from telemetry.internal.backends.chrome import cros_browser_finder
13from telemetry.internal.backends.chrome import desktop_browser_finder
14from telemetry.internal.backends.chrome import ios_browser_finder
15from telemetry.internal.browser import browser_finder_exceptions
16from telemetry.internal.platform import device_finder
17
18BROWSER_FINDERS = [
19  desktop_browser_finder,
20  android_browser_finder,
21  cros_browser_finder,
22  ios_browser_finder,
23  ]
24
25
26def FindAllBrowserTypes(options):
27  return reduce(operator.add,
28                [bf.FindAllBrowserTypes(options) for bf in BROWSER_FINDERS])
29
30
31@decorators.Cache
32def FindBrowser(options):
33  """Finds the best PossibleBrowser object given a BrowserOptions object.
34
35  Args:
36    A BrowserOptions object.
37
38  Returns:
39    A PossibleBrowser object.
40
41  Raises:
42    BrowserFinderException: Options improperly set, or an error occurred.
43  """
44  if options.__class__.__name__ == '_FakeBrowserFinderOptions':
45    return options.fake_possible_browser
46  if options.browser_type == 'exact' and options.browser_executable == None:
47    raise browser_finder_exceptions.BrowserFinderException(
48        '--browser=exact requires --browser-executable to be set.')
49  if options.browser_type != 'exact' and options.browser_executable != None:
50    raise browser_finder_exceptions.BrowserFinderException(
51        '--browser-executable requires --browser=exact.')
52
53  if options.browser_type == 'cros-chrome' and options.cros_remote == None:
54    raise browser_finder_exceptions.BrowserFinderException(
55        'browser_type=cros-chrome requires cros_remote be set.')
56  if (options.browser_type != 'cros-chrome' and
57      options.browser_type != 'cros-chrome-guest' and
58      options.cros_remote != None):
59    raise browser_finder_exceptions.BrowserFinderException(
60        '--remote requires --browser=cros-chrome or cros-chrome-guest.')
61
62  devices = device_finder.GetDevicesMatchingOptions(options)
63  browsers = []
64  default_browsers = []
65  for device in devices:
66    for finder in BROWSER_FINDERS:
67      if(options.browser_type and options.browser_type != 'any' and
68         options.browser_type not in finder.FindAllBrowserTypes(options)):
69        continue
70      curr_browsers = finder.FindAllAvailableBrowsers(options, device)
71      new_default_browser = finder.SelectDefaultBrowser(curr_browsers)
72      if new_default_browser:
73        default_browsers.append(new_default_browser)
74      browsers.extend(curr_browsers)
75
76  if options.browser_type == None:
77    if default_browsers:
78      default_browser = sorted(default_browsers,
79                               key=lambda b: b.last_modification_time())[-1]
80
81      logging.warning('--browser omitted. Using most recent local build: %s' %
82                      default_browser.browser_type)
83      default_browser.UpdateExecutableIfNeeded()
84      return default_browser
85
86    if len(browsers) == 1:
87      logging.warning('--browser omitted. Using only available browser: %s' %
88                      browsers[0].browser_type)
89      browsers[0].UpdateExecutableIfNeeded()
90      return browsers[0]
91
92    raise browser_finder_exceptions.BrowserTypeRequiredException(
93        '--browser must be specified. Available browsers:\n%s' %
94        '\n'.join(sorted(set([b.browser_type for b in browsers]))))
95
96  if options.browser_type == 'any':
97    types = FindAllBrowserTypes(options)
98    def CompareBrowsersOnTypePriority(x, y):
99      x_idx = types.index(x.browser_type)
100      y_idx = types.index(y.browser_type)
101      return x_idx - y_idx
102    browsers.sort(CompareBrowsersOnTypePriority)
103    if len(browsers) >= 1:
104      browsers[0].UpdateExecutableIfNeeded()
105      return browsers[0]
106    else:
107      return None
108
109  matching_browsers = [b for b in browsers
110      if b.browser_type == options.browser_type and b.SupportsOptions(options)]
111
112  chosen_browser = None
113  if len(matching_browsers) == 1:
114    chosen_browser = matching_browsers[0]
115  elif len(matching_browsers) > 1:
116    logging.warning('Multiple browsers of the same type found: %s' % (
117                    repr(matching_browsers)))
118    chosen_browser = sorted(matching_browsers,
119                            key=lambda b: b.last_modification_time())[-1]
120
121  if chosen_browser:
122    logging.info('Chose browser: %s' % (repr(chosen_browser)))
123    chosen_browser.UpdateExecutableIfNeeded()
124
125  return chosen_browser
126
127
128@decorators.Cache
129def GetAllAvailableBrowsers(options, device):
130  """Returns a list of available browsers on the device.
131
132  Args:
133    options: A BrowserOptions object.
134    device: The target device, which can be None.
135
136  Returns:
137    A list of browser instances.
138
139  Raises:
140    BrowserFinderException: Options are improperly set, or an error occurred.
141  """
142  if not device:
143    return []
144  possible_browsers = []
145  for browser_finder in BROWSER_FINDERS:
146    possible_browsers.extend(
147      browser_finder.FindAllAvailableBrowsers(options, device))
148  return possible_browsers
149
150
151@decorators.Cache
152def GetAllAvailableBrowserTypes(options):
153  """Returns a list of available browser types.
154
155  Args:
156    options: A BrowserOptions object.
157
158  Returns:
159    A list of browser type strings.
160
161  Raises:
162    BrowserFinderException: Options are improperly set, or an error occurred.
163  """
164  devices = device_finder.GetDevicesMatchingOptions(options)
165  possible_browsers = []
166  for device in devices:
167    possible_browsers.extend(GetAllAvailableBrowsers(options, device))
168  type_list = set([browser.browser_type for browser in possible_browsers])
169  # The reference build should be available for mac, linux and win, but the
170  # desktop browser finder won't return it in the list of browsers.
171  for browser in possible_browsers:
172    if (browser.target_os == 'darwin' or browser.target_os.startswith('linux')
173        or browser.target_os.startswith('win')):
174      type_list.add('reference')
175      break
176  type_list = list(type_list)
177  type_list.sort()
178  return type_list
179