1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import constants 6 import logging 7 import traceback 8 import warnings 9 10 11 # Location where chrome reads command line flags from 12 CHROME_COMMAND_FILE = '/data/local/chrome-command-line' 13 14 class FlagChanger(object): 15 """Changes the flags Chrome runs with. 16 17 There are two different use cases for this file: 18 * Flags are permanently set by calling Set(). 19 * Flags can be temporarily set for a particular set of unit tests. These 20 tests should call Restore() to revert the flags to their original state 21 once the tests have completed. 22 """ 23 24 def __init__(self, android_cmd): 25 self._android_cmd = android_cmd 26 27 # Save the original flags. 28 self._orig_line = self._android_cmd.GetFileContents(CHROME_COMMAND_FILE) 29 if self._orig_line: 30 self._orig_line = self._orig_line[0].strip() 31 32 # Parse out the flags into a list to facilitate adding and removing flags. 33 self._current_flags = self._TokenizeFlags(self._orig_line) 34 35 def Get(self): 36 """Returns list of current flags.""" 37 return self._current_flags 38 39 def Set(self, flags): 40 """Replaces all flags on the current command line with the flags given. 41 42 Args: 43 flags: A list of flags to set, eg. ['--single-process']. 44 """ 45 if flags: 46 assert flags[0] != 'chrome' 47 48 self._current_flags = flags 49 self._UpdateCommandLineFile() 50 51 def AddFlags(self, flags): 52 """Appends flags to the command line if they aren't already there. 53 54 Args: 55 flags: A list of flags to add on, eg. ['--single-process']. 56 """ 57 if flags: 58 assert flags[0] != 'chrome' 59 60 # Avoid appending flags that are already present. 61 for flag in flags: 62 if flag not in self._current_flags: 63 self._current_flags.append(flag) 64 self._UpdateCommandLineFile() 65 66 def RemoveFlags(self, flags): 67 """Removes flags from the command line, if they exist. 68 69 Args: 70 flags: A list of flags to remove, eg. ['--single-process']. Note that we 71 expect a complete match when removing flags; if you want to remove 72 a switch with a value, you must use the exact string used to add 73 it in the first place. 74 """ 75 if flags: 76 assert flags[0] != 'chrome' 77 78 for flag in flags: 79 if flag in self._current_flags: 80 self._current_flags.remove(flag) 81 self._UpdateCommandLineFile() 82 83 def Restore(self): 84 """Restores the flags to their original state.""" 85 self._current_flags = self._TokenizeFlags(self._orig_line) 86 self._UpdateCommandLineFile() 87 88 def _UpdateCommandLineFile(self): 89 """Writes out the command line to the file, or removes it if empty.""" 90 logging.info('Current flags: %s', self._current_flags) 91 92 if self._current_flags: 93 self._android_cmd.SetProtectedFileContents(CHROME_COMMAND_FILE, 94 'chrome ' + 95 ' '.join(self._current_flags)) 96 else: 97 self._android_cmd.RunShellCommand('su -c rm ' + CHROME_COMMAND_FILE) 98 99 def _TokenizeFlags(self, line): 100 """Changes the string containing the command line into a list of flags. 101 102 Follows similar logic to CommandLine.java::tokenizeQuotedArguments: 103 * Flags are split using whitespace, unless the whitespace is within a 104 pair of quotation marks. 105 * Unlike the Java version, we keep the quotation marks around switch 106 values since we need them to re-create the file when new flags are 107 appended. 108 109 Args: 110 line: A string containing the entire command line. The first token is 111 assumed to be the program name. 112 """ 113 if not line: 114 return [] 115 116 tokenized_flags = [] 117 current_flag = "" 118 within_quotations = False 119 120 # Move through the string character by character and build up each flag 121 # along the way. 122 for c in line.strip(): 123 if c is '"': 124 if len(current_flag) > 0 and current_flag[-1] == '\\': 125 # Last char was a backslash; pop it, and treat this " as a literal. 126 current_flag = current_flag[0:-1] + '"' 127 else: 128 within_quotations = not within_quotations 129 current_flag += c 130 elif not within_quotations and (c is ' ' or c is '\t'): 131 if current_flag is not "": 132 tokenized_flags.append(current_flag) 133 current_flag = "" 134 else: 135 current_flag += c 136 137 # Tack on the last flag. 138 if not current_flag: 139 if within_quotations: 140 warnings.warn("Unterminated quoted string: " + current_flag) 141 else: 142 tokenized_flags.append(current_flag) 143 144 # Return everything but the program name. 145 return tokenized_flags[1:] 146