15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os.path
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shutil
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tempfile
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import urlparse
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import browserprocess
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LaunchFailure(Exception):
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GetPlatform():
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if sys.platform == 'darwin':
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    platform = 'mac'
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif sys.platform.startswith('linux'):
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    platform = 'linux'
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif sys.platform in ('cygwin', 'win32'):
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    platform = 'windows'
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise LaunchFailure('Unknown platform: %s' % sys.platform)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return platform
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PLATFORM = GetPlatform()
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def SelectRunCommand():
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The subprocess module added support for .kill in Python 2.6
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert (sys.version_info[0] >= 3 or (sys.version_info[0] == 2 and
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       sys.version_info[1] >= 6))
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if PLATFORM == 'linux':
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return browserprocess.RunCommandInProcessGroup
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return browserprocess.RunCommandWithSubprocess
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RunCommand = SelectRunCommand()
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RemoveDirectory(path):
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  retry = 5
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sleep_time = 0.25
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while True:
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      shutil.rmtree(path)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception:
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Windows processes sometime hang onto files too long
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if retry > 0:
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        retry -= 1
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        time.sleep(sleep_time)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sleep_time *= 2
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # No luck - don't mask the error
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # succeeded
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# In Windows, subprocess seems to have an issue with file names that
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# contain spaces.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def EscapeSpaces(path):
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if PLATFORM == 'windows' and ' ' in path:
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '"%s"' % path
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return path
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MakeEnv(options):
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  env = dict(os.environ)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Enable PPAPI Dev interfaces for testing.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  env['NACL_ENABLE_PPAPI_DEV'] = str(options.enable_ppapi_dev)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.debug:
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    env['NACL_PLUGIN_DEBUG'] = '1'
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # env['NACL_SRPC_DEBUG'] = '1'
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return env
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BrowserLauncher(object):
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WAIT_TIME = 20
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WAIT_STEPS = 80
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SLEEP_TIME = float(WAIT_TIME) / WAIT_STEPS
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, options):
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.options = options
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.profile = None
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.binary = None
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.tool_log_dir = None
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def KnownPath(self):
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def BinaryName(self):
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CreateProfile(self):
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  def MakeCmd(self, url, host, port):
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CreateToolLogDir(self):
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.tool_log_dir = tempfile.mkdtemp(prefix='vglogs_')
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.tool_log_dir
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def FindBinary(self):
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.browser_path:
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self.options.browser_path
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = self.KnownPath()
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if path is None or not os.path.exists(path):
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise LaunchFailure('Cannot find the browser directory')
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      binary = os.path.join(path, self.BinaryName())
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not os.path.exists(binary):
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise LaunchFailure('Cannot find the browser binary')
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return binary
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def WaitForProcessDeath(self):
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.browser_process.Wait(self.WAIT_STEPS, self.SLEEP_TIME)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Cleanup(self):
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.browser_process.Kill()
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RemoveDirectory(self.profile)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.tool_log_dir is not None:
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RemoveDirectory(self.tool_log_dir)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def MakeProfileDirectory(self):
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.profile = tempfile.mkdtemp(prefix='browserprofile_')
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.profile
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SetStandardStream(self, env, var_name, redirect_file, is_output):
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if redirect_file is None:
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_prefix = 'file:'
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dev_prefix = 'dev:'
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    debug_warning = 'DEBUG_ONLY:'
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # logic must match src/trusted/service_runtime/nacl_resource.*
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # resource specification notation.  file: is the default
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # interpretation, so we must have an exhaustive list of
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # alternative schemes accepted.  if we remove the file-is-default
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # interpretation, replace with
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #   is_file = redirect_file.startswith(file_prefix)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # and remove the list of non-file schemes.
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is_file = (not (redirect_file.startswith(dev_prefix) or
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    redirect_file.startswith(debug_warning + dev_prefix)))
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if is_file:
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if redirect_file.startswith(file_prefix):
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bare_file = redirect_file[len(file_prefix)]
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bare_file = redirect_file
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # why always abspath?  does chrome chdir or might it in the
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # future?  this means we do not test/use the relative path case.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      redirect_file = file_prefix + os.path.abspath(bare_file)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bare_file = None  # ensure error if used without checking is_file
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    env[var_name] = redirect_file
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if is_output:
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # sel_ldr appends program output to the file so we need to clear it
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # in order to get the stable result.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if is_file:
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if os.path.exists(bare_file):
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          os.remove(bare_file)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        parent_dir = os.path.dirname(bare_file)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # parent directory may not exist.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if not os.path.exists(parent_dir):
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          os.makedirs(parent_dir)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Launch(self, cmd, env):
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    browser_path = cmd[0]
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.path.exists(browser_path):
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise LaunchFailure('Browser does not exist %r'% browser_path)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not os.access(browser_path, os.X_OK):
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise LaunchFailure('Browser cannot be executed %r (Is this binary on an '
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          'NFS volume?)' % browser_path)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.sel_ldr:
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      env['NACL_SEL_LDR'] = self.options.sel_ldr
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.sel_ldr_bootstrap:
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      env['NACL_SEL_LDR_BOOTSTRAP'] = self.options.sel_ldr_bootstrap
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.irt_library:
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      env['NACL_IRT_LIBRARY'] = self.options.irt_library
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.SetStandardStream(env, 'NACL_EXE_STDIN',
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self.options.nacl_exe_stdin, False)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.SetStandardStream(env, 'NACL_EXE_STDOUT',
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self.options.nacl_exe_stdout, True)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.SetStandardStream(env, 'NACL_EXE_STDERR',
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           self.options.nacl_exe_stderr, True)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'ENV:', ' '.join(['='.join(pair) for pair in env.iteritems()])
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'LAUNCHING: %s' % ' '.join(cmd)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sys.stdout.flush()
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.browser_process = RunCommand(cmd, env=env)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def IsRunning(self):
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.browser_process.IsRunning()
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetReturnCode(self):
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.browser_process.GetReturnCode()
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  def Run(self, url, host, port):
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.binary = EscapeSpaces(self.FindBinary())
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.profile = self.CreateProfile()
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.tool is not None:
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.tool_log_dir = self.CreateToolLogDir()
2123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    cmd = self.MakeCmd(url, host, port)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.Launch(cmd, MakeEnv(self.options))
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def EnsureDirectory(path):
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not os.path.exists(path):
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.makedirs(path)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def EnsureDirectoryForFile(path):
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnsureDirectory(os.path.dirname(path))
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ChromeLauncher(BrowserLauncher):
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def KnownPath(self):
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if PLATFORM == 'linux':
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # TODO(ncbray): look in path?
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return '/opt/google/chrome'
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif PLATFORM == 'mac':
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return '/Applications/Google Chrome.app/Contents/MacOS'
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      homedir = os.path.expanduser('~')
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = os.path.join(homedir, r'AppData\Local\Google\Chrome\Application')
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return path
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def BinaryName(self):
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if PLATFORM == 'mac':
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 'Google Chrome'
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif PLATFORM == 'windows':
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 'chrome.exe'
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 'chrome'
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def MakeEmptyJSONFile(self, path):
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnsureDirectoryForFile(path)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f = open(path, 'w')
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f.write('{}')
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    f.close()
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CreateProfile(self):
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    profile = self.MakeProfileDirectory()
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Squelch warnings by creating bogus files.
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.MakeEmptyJSONFile(os.path.join(profile, 'Default', 'Preferences'))
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.MakeEmptyJSONFile(os.path.join(profile, 'Local State'))
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return profile
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def NetLogName(self):
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return os.path.join(self.profile, 'netlog.json')
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  def MakeCmd(self, url, host, port):
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cmd = [self.binary,
266f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            # --enable-logging enables stderr output from Chromium subprocesses
267f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            # on Windows (see
268f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            # https://code.google.com/p/chromium/issues/detail?id=171836)
269f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            '--enable-logging',
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            '--disable-web-resources',
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            '--disable-preconnect',
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            # This is speculative, sync should not occur with a clean profile.
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            '--disable-sync',
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            # This prevents Chrome from making "hidden" network requests at
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            # startup.  These requests could be a source of non-determinism,
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            # and they also add noise to the netlogs.
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            '--dns-prefetch-disable',
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            '--no-first-run',
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            '--no-default-browser-check',
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            '--log-level=1',
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            '--safebrowsing-disable-auto-update',
2823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)            '--disable-default-apps',
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            # Suppress metrics reporting.  This prevents misconfigured bots,
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            # people testing at their desktop, etc from poisoning the UMA data.
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            '--metrics-recording-only',
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # Chrome explicitly blacklists some ports as "unsafe" because
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # certain protocols use them.  Chrome gives an error like this:
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # Error 312 (net::ERR_UNSAFE_PORT): Unknown error
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # Unfortunately, the browser tester can randomly choose a
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # blacklisted port.  To work around this, the tester whitelists
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # whatever port it is using.
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            '--explicitly-allowed-ports=%d' % port,
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            '--user-data-dir=%s' % self.profile]
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Log network requests to assist debugging.
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cmd.append('--log-net-log=%s' % self.NetLogName())
2964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if PLATFORM == 'linux':
2974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      # Explicitly run with mesa on linux. The test infrastructure doesn't have
2984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      # sufficient native GL contextes to run these tests.
2994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      cmd.append('--use-gl=osmesa')
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.ppapi_plugin is None:
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd.append('--enable-nacl')
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      disable_sandbox = False
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Chrome process can't access file within sandbox
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      disable_sandbox |= self.options.nacl_exe_stdin is not None
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      disable_sandbox |= self.options.nacl_exe_stdout is not None
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      disable_sandbox |= self.options.nacl_exe_stderr is not None
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if disable_sandbox:
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cmd.append('--no-sandbox')
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
310424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      cmd.append('--register-pepper-plugins=%s;%s'
311424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                 % (self.options.ppapi_plugin,
312424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                    self.options.ppapi_plugin_mimetype))
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd.append('--no-sandbox')
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.browser_extensions:
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd.append('--load-extension=%s' %
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 ','.join(self.options.browser_extensions))
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd.append('--enable-experimental-extension-apis')
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self.options.enable_crash_reporter:
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cmd.append('--enable-crash-reporter-for-testing')
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.options.tool == 'memcheck':
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd = ['src/third_party/valgrind/memcheck.sh',
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '-v',
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--xml=yes',
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--leak-check=no',
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--gen-suppressions=all',
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--num-callers=30',
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--trace-children=yes',
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--nacl-file=%s' % (self.options.files[0],),
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--suppressions=' +
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '../tools/valgrind/memcheck/suppressions.txt',
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--xml-file=%s/xml.%%p' % (self.tool_log_dir,),
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--log-file=%s/log.%%p' % (self.tool_log_dir,)] + cmd
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.tool == 'tsan':
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cmd = ['src/third_party/valgrind/tsan.sh',
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '-v',
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--num-callers=30',
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--trace-children=yes',
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--nacl-file=%s' % (self.options.files[0],),
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--ignore=../tools/valgrind/tsan/ignores.txt',
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--suppressions=../tools/valgrind/tsan/suppressions.txt',
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             '--log-file=%s/log.%%p' % (self.tool_log_dir,)] + cmd
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif self.options.tool != None:
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise LaunchFailure('Invalid tool name "%s"' % (self.options.tool,))
3443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if self.options.enable_sockets:
3453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      cmd.append('--allow-nacl-socket-api=%s' % host)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cmd.extend(self.options.browser_flags)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cmd.append(url)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return cmd
349