Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 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. |
| 4 | import logging |
| 5 | import os |
| 6 | import subprocess as subprocess |
| 7 | import shutil |
| 8 | import sys |
| 9 | import tempfile |
| 10 | |
| 11 | from telemetry.core import util |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 12 | from telemetry.core.backends import browser_backend |
| 13 | from telemetry.core.backends.chrome import chrome_browser_backend |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 14 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 15 | class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 16 | """The backend for controlling a locally-executed browser instance, on Linux, |
| 17 | Mac or Windows. |
| 18 | """ |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 19 | def __init__(self, options, executable, flash_path, is_content_shell, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 20 | browser_directory, delete_profile_dir_after_run=True): |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 21 | super(DesktopBrowserBackend, self).__init__( |
| 22 | is_content_shell=is_content_shell, |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 23 | supports_extensions=not is_content_shell, |
| 24 | options=options) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 25 | |
| 26 | # Initialize fields so that an explosion during init doesn't break in Close. |
| 27 | self._proc = None |
| 28 | self._tmpdir = None |
| 29 | self._tmp_output_file = None |
| 30 | |
| 31 | self._executable = executable |
| 32 | if not self._executable: |
| 33 | raise Exception('Cannot create browser, no executable found!') |
| 34 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 35 | self._flash_path = flash_path |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 36 | if self._flash_path and not os.path.exists(self._flash_path): |
| 37 | logging.warning(('Could not find flash at %s. Running without flash.\n\n' |
| 38 | 'To fix this see http://go/read-src-internal') % |
| 39 | self._flash_path) |
| 40 | self._flash_path = None |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 41 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 42 | if len(options.extensions_to_load) > 0 and is_content_shell: |
| 43 | raise browser_backend.ExtensionsNotSupportedException( |
| 44 | 'Content shell does not support extensions.') |
| 45 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 46 | self._browser_directory = browser_directory |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 47 | self._port = util.GetAvailableLocalPort() |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 48 | self._profile_dir = None |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 49 | self._supports_net_benchmarking = True |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 50 | self._delete_profile_dir_after_run = delete_profile_dir_after_run |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 51 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 52 | self._SetupProfile() |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 53 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 54 | def _SetupProfile(self): |
| 55 | if not self.options.dont_override_profile: |
| 56 | self._tmpdir = tempfile.mkdtemp() |
| 57 | profile_dir = self._profile_dir or self.options.profile_dir |
| 58 | if profile_dir: |
| 59 | if self.is_content_shell: |
| 60 | logging.critical('Profiles cannot be used with content shell') |
| 61 | sys.exit(1) |
| 62 | shutil.rmtree(self._tmpdir) |
| 63 | shutil.copytree(profile_dir, self._tmpdir) |
| 64 | |
| 65 | def _LaunchBrowser(self): |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 66 | args = [self._executable] |
| 67 | args.extend(self.GetBrowserStartupArgs()) |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 68 | if not self.options.show_stdout: |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 69 | self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0) |
| 70 | self._proc = subprocess.Popen( |
| 71 | args, stdout=self._tmp_output_file, stderr=subprocess.STDOUT) |
| 72 | else: |
| 73 | self._proc = subprocess.Popen(args) |
| 74 | |
| 75 | try: |
| 76 | self._WaitForBrowserToComeUp() |
| 77 | self._PostBrowserStartupInitialization() |
| 78 | except: |
| 79 | self.Close() |
| 80 | raise |
| 81 | |
| 82 | def GetBrowserStartupArgs(self): |
| 83 | args = super(DesktopBrowserBackend, self).GetBrowserStartupArgs() |
| 84 | args.append('--remote-debugging-port=%i' % self._port) |
| 85 | if not self.is_content_shell: |
| 86 | args.append('--window-size=1280,1024') |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 87 | if self._flash_path: |
| 88 | args.append('--ppapi-flash-path=%s' % self._flash_path) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 89 | if self._supports_net_benchmarking: |
| 90 | args.append('--enable-net-benchmarking') |
| 91 | else: |
| 92 | args.append('--enable-benchmarking') |
| 93 | if not self.options.dont_override_profile: |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 94 | args.append('--user-data-dir=%s' % self._tmpdir) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 95 | return args |
| 96 | |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 97 | def SetProfileDirectory(self, profile_dir): |
| 98 | # Make sure _profile_dir hasn't already been set. |
| 99 | assert self._profile_dir is None |
| 100 | |
| 101 | if self.is_content_shell: |
| 102 | logging.critical('Profile creation cannot be used with content shell') |
| 103 | sys.exit(1) |
| 104 | |
| 105 | self._profile_dir = profile_dir |
| 106 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 107 | def Start(self): |
| 108 | self._LaunchBrowser() |
| 109 | |
| 110 | # For old chrome versions, might have to relaunch to have the |
| 111 | # correct net_benchmarking switch. |
| 112 | if self._chrome_branch_number < 1418: |
| 113 | self.Close() |
| 114 | self._supports_net_benchmarking = False |
| 115 | self._LaunchBrowser() |
| 116 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 117 | @property |
| 118 | def pid(self): |
| 119 | if self._proc: |
| 120 | return self._proc.pid |
| 121 | return None |
| 122 | |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 123 | @property |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 124 | def browser_directory(self): |
| 125 | return self._browser_directory |
| 126 | |
| 127 | @property |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 128 | def profile_directory(self): |
| 129 | return self._tmpdir |
| 130 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 131 | def IsBrowserRunning(self): |
| 132 | return self._proc.poll() == None |
| 133 | |
| 134 | def GetStandardOutput(self): |
| 135 | assert self._tmp_output_file, "Can't get standard output with show_stdout" |
| 136 | self._tmp_output_file.flush() |
| 137 | try: |
| 138 | with open(self._tmp_output_file.name) as f: |
| 139 | return f.read() |
| 140 | except IOError: |
| 141 | return '' |
| 142 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 143 | def GetStackTrace(self): |
| 144 | # crbug.com/223572, symbolize stack trace for desktop browsers. |
| 145 | logging.warning('Stack traces not supported on desktop browsers, ' |
| 146 | 'returning stdout') |
| 147 | return self.GetStandardOutput() |
| 148 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 149 | def __del__(self): |
| 150 | self.Close() |
| 151 | |
| 152 | def Close(self): |
| 153 | super(DesktopBrowserBackend, self).Close() |
| 154 | |
| 155 | if self._proc: |
| 156 | |
| 157 | def IsClosed(): |
| 158 | if not self._proc: |
| 159 | return True |
| 160 | return self._proc.poll() != None |
| 161 | |
| 162 | # Try to politely shutdown, first. |
| 163 | self._proc.terminate() |
| 164 | try: |
| 165 | util.WaitFor(IsClosed, timeout=1) |
| 166 | self._proc = None |
| 167 | except util.TimeoutException: |
| 168 | pass |
| 169 | |
| 170 | # Kill it. |
| 171 | if not IsClosed(): |
| 172 | self._proc.kill() |
| 173 | try: |
| 174 | util.WaitFor(IsClosed, timeout=5) |
| 175 | self._proc = None |
| 176 | except util.TimeoutException: |
| 177 | self._proc = None |
| 178 | raise Exception('Could not shutdown the browser.') |
| 179 | |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 180 | if self._delete_profile_dir_after_run and \ |
| 181 | self._tmpdir and os.path.exists(self._tmpdir): |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 182 | shutil.rmtree(self._tmpdir, ignore_errors=True) |
| 183 | self._tmpdir = None |
| 184 | |
| 185 | if self._tmp_output_file: |
| 186 | self._tmp_output_file.close() |
| 187 | self._tmp_output_file = None |
| 188 | |
| 189 | def CreateForwarder(self, *port_pairs): |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 190 | return browser_backend.DoNothingForwarder(*port_pairs) |