blob: 28ede858925debf761915abc5997cf2834f03f0e [file] [log] [blame]
# Copyright (c) 2012 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.
import logging
import socket
import subprocess
import time
from chrome_remote_control import browser_backend
from chrome_remote_control import cros_interface
class CrOSBrowserBackend(browser_backend.BrowserBackend):
"""The backend for controlling a browser instance running on CrOS.
"""
def __init__(self, browser_type, options, is_content_shell, cri):
super(CrOSBrowserBackend, self).__init__(is_content_shell)
# Initialize fields so that an explosion during init doesn't break in Close.
self._options = options
assert not is_content_shell
self._cri = cri
self._browser_type = browser_type
tmp = socket.socket()
tmp.bind(('', 0))
self._port = tmp.getsockname()[1]
tmp.close()
self._tmpdir = None
self._X = None
self._proc = None
# TODO(nduca): Stop ui if running.
if self._cri.IsServiceRunning('ui'):
# Note, if this hangs, its probably because they were using wifi AND they
# had a user-specific wifi password, which when you stop ui kills the wifi
# connection.
logging.debug('stopping ui')
self._cri.GetCmdOutput(['stop', 'ui'])
remote_port = self._cri.GetRemotePort()
args = ['/opt/google/chrome/chrome',
'--no-first-run',
'--aura-host-window-use-fullscreen',
'--remote-debugging-port=%i' % remote_port]
if not is_content_shell:
logging.info('Preparing user data dir')
self._tmpdir = '/tmp/chrome_remote_control'
if options.dont_override_profile:
# TODO(nduca): Implement support for this.
logging.critical('Feature not (yet) implemented.')
# Ensure a clean user_data_dir.
self._cri.GetCmdOutput(['rm', '-rf', self._tmpdir])
args.append('--user-data-dir=%s' % self._tmpdir)
# Final bits of command line prep.
args.extend(options.extra_browser_args)
prevent_output = not options.show_stdout
# Stop old X.
logging.info('Stoppping old X')
self._cri.KillAllMatching(
lambda name: name.startswith('/usr/bin/X '))
# Start X.
logging.info('Starting new X')
X_args = ['/usr/bin/X',
'-noreset',
'-nolisten',
'tcp',
'vt01',
'-auth',
'/var/run/chromelogin.auth']
self._X = cros_interface.DeviceSideProcess(
self._cri, X_args, prevent_output=prevent_output)
# Stop old chrome.
logging.info('Killing old chrome')
self._cri.KillAllMatching(
lambda name: name.startswith('/opt/google/chrome/chrome '))
# Start chrome via a bootstrap.
logging.info('Starting chrome')
self._proc = cros_interface.DeviceSideProcess(
self._cri,
args,
prevent_output=prevent_output,
extra_ssh_args=['-L%i:localhost:%i' % (self._port, remote_port)],
leave_ssh_alive=True,
env={'DISPLAY': ':0',
'USER': 'chronos'},
login_shell=True)
# You're done.
try:
self._WaitForBrowserToComeUp()
except:
import traceback
traceback.print_exc()
self.Close()
raise
def __del__(self):
self.Close()
def Close(self):
if self._proc:
self._proc.Close()
self._proc = None
if self._X:
self._X.Close()
self._X = None
if self._tmpdir:
self._cri.GetCmdOutput(['rm', '-rf', self._tmpdir])
self._tmpdir = None
self._cri = None
def IsBrowserRunning(self):
if not self._proc:
return False
return self._proc.IsAlive()
def CreateForwarder(self, host_port):
assert self._cri
return SSHReverseForwarder(self._cri,
host_port)
class SSHReverseForwarder(object):
def __init__(self, cri, host_port):
self._proc = None
# TODO(nduca): Try to pick a remote port that is free in a smater way. This
# is idiotic.
self._remote_port = cri.GetRemotePort()
self._proc = subprocess.Popen(
cri.FormSSHCommandLine(['sleep', '99999999999'],
['-R%i:localhost:%i' %
(self._remote_port, host_port)]),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
shell=False)
# TODO(nduca): How do we wait for the server to come up in a
# robust way?
time.sleep(1.5)
@property
def url(self):
assert self._proc
return 'http://localhost:%i' % self._remote_port
def Close(self):
if self._proc:
self._proc.kill()
self._proc = None