15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import constants
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import logging
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import traceback
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import warnings
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Location where chrome reads command line flags from
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CHROME_COMMAND_FILE = '/data/local/chrome-command-line'
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class FlagChanger(object):
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Changes the flags Chrome runs with.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  There are two different use cases for this file:
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  * Flags are permanently set by calling Set().
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  * Flags can be temporarily set for a particular set of unit tests.  These
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tests should call Restore() to revert the flags to their original state
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    once the tests have completed.
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, android_cmd):
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._android_cmd = android_cmd
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Save the original flags.
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._orig_line = self._android_cmd.GetFileContents(CHROME_COMMAND_FILE)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._orig_line:
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._orig_line = self._orig_line[0].strip()
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Parse out the flags into a list to facilitate adding and removing flags.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._current_flags = self._TokenizeFlags(self._orig_line)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Get(self):
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns list of current flags."""
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._current_flags
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Set(self, flags):
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Replaces all flags on the current command line with the flags given.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      flags: A list of flags to set, eg. ['--single-process'].
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if flags:
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert flags[0] != 'chrome'
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._current_flags = flags
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._UpdateCommandLineFile()
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddFlags(self, flags):
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Appends flags to the command line if they aren't already there.
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      flags: A list of flags to add on, eg. ['--single-process'].
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if flags:
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert flags[0] != 'chrome'
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Avoid appending flags that are already present.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for flag in flags:
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if flag not in self._current_flags:
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._current_flags.append(flag)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._UpdateCommandLineFile()
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def RemoveFlags(self, flags):
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Removes flags from the command line, if they exist.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      flags: A list of flags to remove, eg. ['--single-process'].  Note that we
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             expect a complete match when removing flags; if you want to remove
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             a switch with a value, you must use the exact string used to add
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             it in the first place.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if flags:
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert flags[0] != 'chrome'
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for flag in flags:
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if flag in self._current_flags:
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._current_flags.remove(flag)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._UpdateCommandLineFile()
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Restore(self):
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Restores the flags to their original state."""
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._current_flags = self._TokenizeFlags(self._orig_line)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._UpdateCommandLineFile()
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _UpdateCommandLineFile(self):
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Writes out the command line to the file, or removes it if empty."""
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    logging.info('Current flags: %s', self._current_flags)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._current_flags:
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._android_cmd.SetProtectedFileContents(CHROME_COMMAND_FILE,
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        'chrome ' +
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        ' '.join(self._current_flags))
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._android_cmd.RunShellCommand('su -c rm ' + CHROME_COMMAND_FILE)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _TokenizeFlags(self, line):
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Changes the string containing the command line into a list of flags.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Follows similar logic to CommandLine.java::tokenizeQuotedArguments:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    * Flags are split using whitespace, unless the whitespace is within a
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pair of quotation marks.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    * Unlike the Java version, we keep the quotation marks around switch
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      values since we need them to re-create the file when new flags are
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      appended.
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line: A string containing the entire command line.  The first token is
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            assumed to be the program name.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not line:
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return []
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tokenized_flags = []
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_flag = ""
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    within_quotations = False
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Move through the string character by character and build up each flag
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # along the way.
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for c in line.strip():
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if c is '"':
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if len(current_flag) > 0 and current_flag[-1] == '\\':
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # Last char was a backslash; pop it, and treat this " as a literal.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          current_flag = current_flag[0:-1] + '"'
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          within_quotations = not within_quotations
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          current_flag += c
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif not within_quotations and (c is ' ' or c is '\t'):
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if current_flag is not "":
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          tokenized_flags.append(current_flag)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          current_flag = ""
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_flag += c
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Tack on the last flag.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not current_flag:
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if within_quotations:
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        warnings.warn("Unterminated quoted string: " + current_flag)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tokenized_flags.append(current_flag)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Return everything but the program name.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return tokenized_flags[1:]
146