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"""Finds desktop browsers that can be controlled by telemetry."""
5
6import logging
7import os
8import subprocess
9import sys
10
11from telemetry.core import platform as platform_module
12from telemetry.core import browser
13from telemetry.core import possible_browser
14from telemetry.core.backends.chrome import desktop_browser_backend
15from telemetry.util import path
16
17
18class PossibleDesktopBrowser(possible_browser.PossibleBrowser):
19  """A desktop browser that can be controlled."""
20
21  def __init__(self, browser_type, finder_options, executable, flash_path,
22               is_content_shell, browser_directory, is_local_build=False):
23    target_os = sys.platform.lower()
24    super(PossibleDesktopBrowser, self).__init__(browser_type, target_os,
25        finder_options, not is_content_shell)
26    assert browser_type in FindAllBrowserTypes(finder_options), \
27        ('Please add %s to desktop_browser_finder.FindAllBrowserTypes' %
28          browser_type)
29    self._local_executable = executable
30    self._flash_path = flash_path
31    self._is_content_shell = is_content_shell
32    self._browser_directory = browser_directory
33    self.is_local_build = is_local_build
34
35  def __repr__(self):
36    return 'PossibleDesktopBrowser(type=%s, executable=%s, flash=%s)' % (
37        self.browser_type, self._local_executable, self._flash_path)
38
39  def _InitPlatformIfNeeded(self):
40    if self._platform:
41      return
42
43    self._platform = platform_module.GetHostPlatform()
44
45    # pylint: disable=W0212
46    self._platform_backend = self._platform._platform_backend
47
48  def Create(self):
49    if self._flash_path and not os.path.exists(self._flash_path):
50      logging.warning(
51          'Could not find Flash at %s. Continuing without Flash.\n'
52          'To run with Flash, check it out via http://go/read-src-internal',
53          self._flash_path)
54      self._flash_path = None
55
56    self._InitPlatformIfNeeded()
57
58    backend = desktop_browser_backend.DesktopBrowserBackend(
59        self.finder_options.browser_options, self._local_executable,
60        self._flash_path, self._is_content_shell, self._browser_directory,
61        output_profile_path=self.finder_options.output_profile_path,
62        extensions_to_load=self.finder_options.extensions_to_load)
63    return browser.Browser(backend,
64                           self._platform_backend,
65                           self._archive_path,
66                           self._append_to_existing_wpr,
67                           self._make_javascript_deterministic,
68                           self._credentials_path)
69
70  def SupportsOptions(self, finder_options):
71    if (len(finder_options.extensions_to_load) != 0) and self._is_content_shell:
72      return False
73    return True
74
75  def UpdateExecutableIfNeeded(self):
76    pass
77
78  def last_modification_time(self):
79    if os.path.exists(self._local_executable):
80      return os.path.getmtime(self._local_executable)
81    return -1
82
83def SelectDefaultBrowser(possible_browsers):
84  local_builds_by_date = [
85      b for b in sorted(possible_browsers,
86                        key=lambda b: b.last_modification_time())
87      if b.is_local_build]
88  if local_builds_by_date:
89    return local_builds_by_date[-1]
90  return None
91
92def CanFindAvailableBrowsers():
93  return not platform_module.GetHostPlatform().GetOSName() == 'chromeos'
94
95def FindAllBrowserTypes(_):
96  return [
97      'exact',
98      'reference',
99      'release',
100      'release_x64',
101      'debug',
102      'debug_x64',
103      'canary',
104      'content-shell-debug',
105      'content-shell-debug_x64',
106      'content-shell-release',
107      'content-shell-release_x64',
108      'system']
109
110def FindAllAvailableBrowsers(finder_options):
111  """Finds all the desktop browsers available on this machine."""
112  browsers = []
113
114  if not CanFindAvailableBrowsers():
115    return []
116
117  has_display = True
118  if (sys.platform.startswith('linux') and
119      os.getenv('DISPLAY') == None):
120    has_display = False
121
122  # Look for a browser in the standard chrome build locations.
123  if finder_options.chrome_root:
124    chrome_root = finder_options.chrome_root
125  else:
126    chrome_root = path.GetChromiumSrcDir()
127
128  flash_bin_dir = os.path.join(
129      chrome_root, 'third_party', 'adobe', 'flash', 'binaries', 'ppapi')
130
131  chromium_app_names = []
132  if sys.platform == 'darwin':
133    chromium_app_names.append('Chromium.app/Contents/MacOS/Chromium')
134    chromium_app_names.append('Google Chrome.app/Contents/MacOS/Google Chrome')
135    content_shell_app_name = 'Content Shell.app/Contents/MacOS/Content Shell'
136    flash_bin = 'PepperFlashPlayer.plugin'
137    flash_path = os.path.join(flash_bin_dir, 'mac', flash_bin)
138    flash_path_64 = os.path.join(flash_bin_dir, 'mac_64', flash_bin)
139  elif sys.platform.startswith('linux'):
140    chromium_app_names.append('chrome')
141    content_shell_app_name = 'content_shell'
142    flash_bin = 'libpepflashplayer.so'
143    flash_path = os.path.join(flash_bin_dir, 'linux', flash_bin)
144    flash_path_64 = os.path.join(flash_bin_dir, 'linux_x64', flash_bin)
145  elif sys.platform.startswith('win'):
146    chromium_app_names.append('chrome.exe')
147    content_shell_app_name = 'content_shell.exe'
148    flash_bin = 'pepflashplayer.dll'
149    flash_path = os.path.join(flash_bin_dir, 'win', flash_bin)
150    flash_path_64 = os.path.join(flash_bin_dir, 'win_x64', flash_bin)
151  else:
152    raise Exception('Platform not recognized')
153
154  # Add the explicit browser executable if given.
155  if finder_options.browser_executable:
156    normalized_executable = os.path.expanduser(
157        finder_options.browser_executable)
158    if path.IsExecutable(normalized_executable):
159      browser_directory = os.path.dirname(finder_options.browser_executable)
160      browsers.append(PossibleDesktopBrowser('exact', finder_options,
161                                             normalized_executable, flash_path,
162                                             False, browser_directory))
163    else:
164      raise Exception('%s specified by --browser-executable does not exist',
165                      normalized_executable)
166
167  def AddIfFound(browser_type, build_dir, type_dir, app_name, content_shell):
168    browser_directory = os.path.join(chrome_root, build_dir, type_dir)
169    app = os.path.join(browser_directory, app_name)
170    if path.IsExecutable(app):
171      is_64 = browser_type.endswith('_x64')
172      browsers.append(PossibleDesktopBrowser(
173          browser_type, finder_options, app,
174          flash_path_64 if is_64 else flash_path,
175          content_shell, browser_directory, is_local_build=True))
176      return True
177    return False
178
179  # Add local builds
180  for build_dir, build_type in path.GetBuildDirectories():
181    for chromium_app_name in chromium_app_names:
182      AddIfFound(build_type.lower(), build_dir, build_type,
183                 chromium_app_name, False)
184    AddIfFound('content-shell-' + build_type.lower(), build_dir, build_type,
185               content_shell_app_name, True)
186
187  reference_build_root = os.path.join(
188     chrome_root, 'chrome', 'tools', 'test', 'reference_build')
189
190  # Mac-specific options.
191  if sys.platform == 'darwin':
192    mac_canary_root = '/Applications/Google Chrome Canary.app/'
193    mac_canary = mac_canary_root + 'Contents/MacOS/Google Chrome Canary'
194    mac_system_root = '/Applications/Google Chrome.app'
195    mac_system = mac_system_root + '/Contents/MacOS/Google Chrome'
196    mac_reference_root = reference_build_root + '/chrome_mac/Google Chrome.app/'
197    mac_reference = mac_reference_root + 'Contents/MacOS/Google Chrome'
198    if path.IsExecutable(mac_canary):
199      browsers.append(PossibleDesktopBrowser('canary', finder_options,
200                                             mac_canary, None, False,
201                                             mac_canary_root))
202
203    if path.IsExecutable(mac_system):
204      browsers.append(PossibleDesktopBrowser('system', finder_options,
205                                             mac_system, None, False,
206                                             mac_system_root))
207    if path.IsExecutable(mac_reference):
208      browsers.append(PossibleDesktopBrowser('reference', finder_options,
209                                             mac_reference, None, False,
210                                             mac_reference_root))
211
212  # Linux specific options.
213  if sys.platform.startswith('linux'):
214    # Look for a google-chrome instance.
215    found = False
216    try:
217      with open(os.devnull, 'w') as devnull:
218        found = subprocess.call(['google-chrome', '--version'],
219                                stdout=devnull, stderr=devnull) == 0
220    except OSError:
221      pass
222    if found:
223      browsers.append(PossibleDesktopBrowser('system', finder_options,
224                                             'google-chrome', None, False,
225                                             '/opt/google/chrome'))
226    linux_reference_root = os.path.join(reference_build_root, 'chrome_linux')
227    linux_reference = os.path.join(linux_reference_root, 'chrome')
228    if path.IsExecutable(linux_reference):
229      browsers.append(PossibleDesktopBrowser('reference', finder_options,
230                                             linux_reference, None, False,
231                                             linux_reference_root))
232
233  # Win32-specific options.
234  if sys.platform.startswith('win'):
235    app_paths = (
236        ('system', os.path.join('Google', 'Chrome', 'Application')),
237        ('canary', os.path.join('Google', 'Chrome SxS', 'Application')),
238        ('reference', os.path.join(reference_build_root, 'chrome_win')),
239    )
240
241    for browser_name, app_path in app_paths:
242      for chromium_app_name in chromium_app_names:
243        app_path = os.path.join(app_path, chromium_app_name)
244        app_path = path.FindInstalledWindowsApplication(app_path)
245        if app_path:
246          browsers.append(PossibleDesktopBrowser(
247              browser_name, finder_options, app_path,
248              None, False, os.path.dirname(app_path)))
249
250  if len(browsers) and not has_display:
251    logging.warning(
252      'Found (%s), but you do not have a DISPLAY environment set.' %
253      ','.join([b.browser_type for b in browsers]))
254    return []
255
256  return browsers
257