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.core.backends.chrome import android_browser_finder
12from telemetry.core.backends.chrome import cros_browser_finder
13from telemetry.core.backends.chrome import desktop_browser_finder
14from telemetry.core.backends.chrome import ios_browser_finder
15from telemetry.core.backends.remote import trybot_browser_finder
16from telemetry.core.backends.webdriver import webdriver_desktop_browser_finder
17
18BROWSER_FINDERS = [
19  desktop_browser_finder,
20  android_browser_finder,
21  cros_browser_finder,
22  ios_browser_finder,
23  trybot_browser_finder,
24  webdriver_desktop_browser_finder,
25  ]
26
27
28class BrowserTypeRequiredException(Exception):
29  pass
30
31
32class BrowserFinderException(Exception):
33  pass
34
35
36def FindAllBrowserTypes(options):
37  return reduce(operator.add,
38                [bf.FindAllBrowserTypes(options) for bf in BROWSER_FINDERS])
39
40
41@decorators.Cache
42def FindBrowser(options):
43  """Finds the best PossibleBrowser object given a BrowserOptions object.
44
45  Args:
46    A BrowserOptions object.
47
48  Returns:
49    A PossibleBrowser object.
50
51  Raises:
52    BrowserFinderException: Options improperly set, or an error occurred.
53  """
54  if options.browser_type == 'exact' and options.browser_executable == None:
55    raise BrowserFinderException(
56        '--browser=exact requires --browser-executable to be set.')
57  if options.browser_type != 'exact' and options.browser_executable != None:
58    raise BrowserFinderException(
59        '--browser-executable requires --browser=exact.')
60
61  if options.browser_type == 'cros-chrome' and options.cros_remote == None:
62    raise BrowserFinderException(
63        'browser_type=cros-chrome requires cros_remote be set.')
64  if (options.browser_type != 'cros-chrome' and
65      options.browser_type != 'cros-chrome-guest' and
66      options.cros_remote != None):
67    raise BrowserFinderException(
68        '--remote requires --browser=cros-chrome or cros-chrome-guest.')
69
70  browsers = []
71  default_browsers = []
72  for finder in BROWSER_FINDERS:
73    if(options.browser_type and options.browser_type != 'any' and
74       options.browser_type not in finder.FindAllBrowserTypes(options)):
75      continue
76    curr_browsers = finder.FindAllAvailableBrowsers(options)
77    new_default_browser = finder.SelectDefaultBrowser(curr_browsers)
78    if new_default_browser:
79      default_browsers.append(new_default_browser)
80    browsers.extend(curr_browsers)
81
82  if options.browser_type == None:
83    if default_browsers:
84      default_browser = sorted(default_browsers,
85                               key=lambda b: b.last_modification_time())[-1]
86
87      logging.warning('--browser omitted. Using most recent local build: %s' %
88                      default_browser.browser_type)
89      default_browser.UpdateExecutableIfNeeded()
90      return default_browser
91
92    if len(browsers) == 1:
93      logging.warning('--browser omitted. Using only available browser: %s' %
94                      browsers[0].browser_type)
95      browsers[0].UpdateExecutableIfNeeded()
96      return browsers[0]
97
98    raise BrowserTypeRequiredException(
99        '--browser must be specified. Available browsers:\n%s' %
100        '\n'.join(sorted(set([b.browser_type for b in browsers]))))
101
102  if options.browser_type == 'any':
103    types = FindAllBrowserTypes(options)
104    def CompareBrowsersOnTypePriority(x, y):
105      x_idx = types.index(x.browser_type)
106      y_idx = types.index(y.browser_type)
107      return x_idx - y_idx
108    browsers.sort(CompareBrowsersOnTypePriority)
109    if len(browsers) >= 1:
110      browsers[0].UpdateExecutableIfNeeded()
111      return browsers[0]
112    else:
113      return None
114
115  matching_browsers = [b for b in browsers
116      if b.browser_type == options.browser_type and b.SupportsOptions(options)]
117
118  chosen_browser = None
119  if len(matching_browsers) == 1:
120    chosen_browser = matching_browsers[0]
121  elif len(matching_browsers) > 1:
122    logging.warning('Multiple browsers of the same type found: %s' % (
123                    repr(matching_browsers)))
124    chosen_browser = sorted(matching_browsers,
125                            key=lambda b: b.last_modification_time())[-1]
126
127  if chosen_browser:
128    logging.info('Chose browser: %s' % (repr(chosen_browser)))
129    chosen_browser.UpdateExecutableIfNeeded()
130
131  return chosen_browser
132
133
134@decorators.Cache
135def GetAllAvailableBrowserTypes(options):
136  """Returns a list of available browser types.
137
138  Args:
139    options: A BrowserOptions object.
140
141  Returns:
142    A list of browser type strings.
143
144  Raises:
145    BrowserFinderException: Options are improperly set, or an error occurred.
146  """
147  browsers = []
148  for finder in BROWSER_FINDERS:
149    browsers.extend(finder.FindAllAvailableBrowsers(options))
150
151  type_list = set([browser.browser_type for browser in browsers])
152  type_list = list(type_list)
153  type_list.sort()
154  return type_list
155