| #!/usr/bin/env python | 
 | # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | from __future__ import print_function | 
 |  | 
 | import os | 
 | import signal | 
 | import subprocess | 
 | import time | 
 |  | 
 | class BrowserProcessBase(object): | 
 |  | 
 |   def __init__(self, handle): | 
 |     self.handle = handle | 
 |     print('PID', self.handle.pid) | 
 |  | 
 |   def GetReturnCode(self): | 
 |     return self.handle.returncode | 
 |  | 
 |   def IsRunning(self): | 
 |     return self.handle.poll() is None | 
 |  | 
 |   def Wait(self, wait_steps, sleep_time): | 
 |     try: | 
 |       self.term() | 
 |     except Exception: | 
 |       # Terminating the handle can raise an exception. There is likely no point | 
 |       # in waiting if the termination didn't succeed. | 
 |       return | 
 |  | 
 |     i = 0 | 
 |     # subprocess.wait() doesn't have a timeout, unfortunately. | 
 |     while self.IsRunning() and i < wait_steps: | 
 |       time.sleep(sleep_time) | 
 |       i += 1 | 
 |  | 
 |   def Kill(self): | 
 |     if self.IsRunning(): | 
 |       print('KILLING the browser') | 
 |       try: | 
 |         self.kill() | 
 |         # If it doesn't die, we hang.  Oh well. | 
 |         self.handle.wait() | 
 |       except Exception: | 
 |         # If it is already dead, then it's ok. | 
 |         # This may happen if the browser dies after the first poll, but | 
 |         # before the kill. | 
 |         if self.IsRunning(): | 
 |           raise | 
 |  | 
 | class BrowserProcess(BrowserProcessBase): | 
 |  | 
 |   def term(self): | 
 |     self.handle.terminate() | 
 |  | 
 |   def kill(self): | 
 |     self.handle.kill() | 
 |  | 
 |  | 
 | class BrowserProcessPosix(BrowserProcessBase): | 
 |   """ This variant of BrowserProcess uses process groups to manage browser | 
 |   life time. """ | 
 |  | 
 |   def term(self): | 
 |     os.killpg(self.handle.pid, signal.SIGTERM) | 
 |  | 
 |   def kill(self): | 
 |     os.killpg(self.handle.pid, signal.SIGKILL) | 
 |  | 
 |  | 
 | def RunCommandWithSubprocess(cmd, env=None): | 
 |   handle = subprocess.Popen(cmd, env=env) | 
 |   return BrowserProcess(handle) | 
 |  | 
 |  | 
 | def RunCommandInProcessGroup(cmd, env=None): | 
 |   def SetPGrp(): | 
 |     os.setpgrp() | 
 |     print('I\'M THE SESSION LEADER!') | 
 |  | 
 |   handle = subprocess.Popen(cmd, env=env, preexec_fn=SetPGrp) | 
 |   return BrowserProcessPosix(handle) |