1 # Copyright (C) 2016 Igalia S.L. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 from webkitpy.benchmark_runner.utils import force_remove
37 from browser_driver import BrowserDriver
40 _log = logging.getLogger(__name__)
43 class LinuxBrowserDriver(BrowserDriver):
45 process_search_list = []
49 self.process_name = self._get_first_executable_path_from_list(self.process_search_list)
50 if self.process_name is None:
51 raise ValueError('Cant find executable for browser {browser_name}. Searched list: {browser_process_list}'.format(
52 browser_name=self.browser_name, browser_process_list=self.process_search_list))
54 def prepare_env(self, device_id):
55 self._browser_process = None
56 self._browser_arguments = None
57 self._temp_profiledir = tempfile.mkdtemp()
58 self._test_environ = dict(os.environ)
59 self._test_environ['HOME'] = self._temp_profiledir
61 def restore_env(self):
62 force_remove(self._temp_profiledir)
64 def close_browsers(self):
65 if self._browser_process:
66 if self._browser_process.poll() is None: # still running
67 if 'psutil' in sys.modules:
68 main_browser_process = psutil.Process(self._browser_process.pid)
69 browser_children = main_browser_process.children(recursive=True)
70 _log.info('Killing browser {browser_name} with pid {browser_pid} and cmd: {browser_cmd}'.format(
71 browser_name=self.browser_name, browser_pid=self._browser_process.pid,
72 browser_cmd=' '.join(main_browser_process.cmdline()).strip() or main_browser_process.name()))
73 main_browser_process.kill()
74 for browser_child in browser_children:
75 if browser_child.is_running():
76 _log.info('Killing still alive {browser_name} child with pid {browser_pid} and cmd: {browser_cmd}'.format(
77 browser_name=self.browser_name, browser_pid=browser_child.pid,
78 browser_cmd=' '.join(browser_child.cmdline()).strip() or browser_child.name()))
81 _log.info('Killing browser {browser_name} with pid {browser_pid}'.format(
82 browser_name=self.browser_name, browser_pid=self._browser_process.pid))
83 self._browser_process.kill()
84 _log.warning('python psutil not found, cant check for '
85 'still-alive browser childs to kill.')
87 _log.error('Browser {browser_name} with pid {browser_pid} ended prematurely with return code {browser_retcode}.'.format(
88 browser_name=self.browser_name, browser_pid=self._browser_process.pid,
89 browser_retcode=self._browser_process.returncode))
91 def launch_url(self, url, options, browser_build_path):
92 if not self._browser_arguments:
93 self._browser_arguments = [url]
94 exec_args = [self.process_name] + self._browser_arguments
95 _log.info('Executing: {browser_cmdline}'.format(browser_cmdline=' '.join(exec_args)))
96 self._browser_process = subprocess.Popen(exec_args, env=self._test_environ,
97 stdout=subprocess.PIPE,
98 stderr=subprocess.STDOUT)
100 def _get_first_executable_path_from_list(self, searchlist):
101 searchpath = [os.path.curdir] + os.environ['PATH'].split(os.pathsep)
102 for program in searchlist:
103 for path in searchpath:
104 fullpath = os.path.abspath(os.path.join(path, program))
105 if os.path.isfile(fullpath) and os.access(fullpath, os.X_OK):
109 def _screen_size(self):
110 # load_subclasses() from __init__.py will load this file to
111 # check the platform defined. Do here a lazy import instead of
112 # trying to import the Gtk module on the global scope of this
113 # file to avoid ImportError errors on other platforms.
114 # Python imports are cached and only run once, so this should be ok.
116 gi.require_version('Gtk', '3.0')
117 gi.require_version('Gdk', '3.0')
118 from gi.repository import Gtk, Gdk
119 if Gtk.get_major_version() == 3 and Gtk.get_minor_version() < 22:
120 screen = Gtk.Window().get_screen()
121 return screen.get_monitor_geometry(screen.get_primary_monitor())
123 display = Gdk.Display.get_default()
124 monitor = display.get_primary_monitor()
125 return monitor.get_geometry()