blob: 834278d015f8e3e44723b97d7489260b0c03a7b0 [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001# 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
5import os
6import subprocess as subprocess
7import shutil
8import sys
9import tempfile
10
11from telemetry.core import util
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010012from telemetry.core.backends import browser_backend
13from telemetry.core.backends.chrome import chrome_browser_backend
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000014
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010015class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000016 """The backend for controlling a locally-executed browser instance, on Linux,
17 Mac or Windows.
18 """
Ben Murdocheb525c52013-07-10 11:40:50 +010019 def __init__(self, options, executable, flash_path, is_content_shell,
Ben Murdochbb1529c2013-08-08 10:24:53 +010020 browser_directory, delete_profile_dir_after_run=True):
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000021 super(DesktopBrowserBackend, self).__init__(
22 is_content_shell=is_content_shell,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010023 supports_extensions=not is_content_shell,
24 options=options)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000025
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 Murdocheb525c52013-07-10 11:40:50 +010035 self._flash_path = flash_path
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010036 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 Murdocheb525c52013-07-10 11:40:50 +010041
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000042 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 Murdochbb1529c2013-08-08 10:24:53 +010046 self._browser_directory = browser_directory
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000047 self._port = util.GetAvailableLocalPort()
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010048 self._profile_dir = None
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000049 self._supports_net_benchmarking = True
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010050 self._delete_profile_dir_after_run = delete_profile_dir_after_run
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000051
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010052 self._SetupProfile()
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000053
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010054 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)2a99a7e2013-03-28 15:31:22 +000066 args = [self._executable]
67 args.extend(self.GetBrowserStartupArgs())
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010068 if not self.options.show_stdout:
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000069 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 Murdocheb525c52013-07-10 11:40:50 +010087 if self._flash_path:
88 args.append('--ppapi-flash-path=%s' % self._flash_path)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000089 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)2a99a7e2013-03-28 15:31:22 +000094 args.append('--user-data-dir=%s' % self._tmpdir)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000095 return args
96
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010097 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)a36e5922013-08-05 13:57:33 +0100107 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)2a99a7e2013-03-28 15:31:22 +0000117 @property
118 def pid(self):
119 if self._proc:
120 return self._proc.pid
121 return None
122
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100123 @property
Ben Murdochbb1529c2013-08-08 10:24:53 +0100124 def browser_directory(self):
125 return self._browser_directory
126
127 @property
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100128 def profile_directory(self):
129 return self._tmpdir
130
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000131 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 Murdoch7dbb3d52013-07-17 14:55:54 +0100143 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)2a99a7e2013-03-28 15:31:22 +0000149 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)90dce4d2013-05-29 14:40:03 +0100180 if self._delete_profile_dir_after_run and \
181 self._tmpdir and os.path.exists(self._tmpdir):
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000182 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)c2e0dbd2013-05-09 18:35:53 +0100190 return browser_backend.DoNothingForwarder(*port_pairs)