- add sources.
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / common / CommandLine.java
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 package org.chromium.content.common;
6
7 import android.text.TextUtils;
8 import android.util.Log;
9
10 import java.io.File;
11 import java.io.FileInputStream;
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.io.InputStreamReader;
15 import java.io.Reader;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.HashMap;
19 import java.util.concurrent.atomic.AtomicReference;
20
21 /**
22  * Java mirror of Chrome command-line utilities (e.g. class CommandLine from base/command_line.h).
23  * Command line program adb_command_line can be used to set the Chrome command line:
24  * adb shell "echo chrome --my-param > /data/local/chrome-command-line"
25  */
26 public abstract class CommandLine {
27     // Block onCreate() of Chrome until a Java debugger is attached.
28     public static final String WAIT_FOR_JAVA_DEBUGGER = "wait-for-java-debugger";
29
30     // Tell Java to use the official command line, loaded from the
31     // official-command-line.xml files.  WARNING this is not done
32     // immediately on startup, so early running Java code will not see
33     // these flags.
34     public static final String ADD_OFFICIAL_COMMAND_LINE = "add-official-command-line";
35
36     // Enables test intent handling.
37     public static final String ENABLE_TEST_INTENTS = "enable-test-intents";
38
39     // Adds additional thread idle time information into the trace event output.
40     public static final String ENABLE_IDLE_TRACING = "enable-idle-tracing";
41
42     // Dump frames-per-second to the log
43     public static final String LOG_FPS = "log-fps";
44
45     // Whether Chromium should use a mobile user agent.
46     public static final String USE_MOBILE_UA = "use-mobile-user-agent";
47
48     // tablet specific UI components.
49     // Native switch - chrome_switches::kTabletUI
50     public static final String TABLET_UI = "tablet-ui";
51
52     // Change the url of the JavaScript that gets injected when accessibility mode is enabled.
53     public static final String ACCESSIBILITY_JAVASCRIPT_URL = "accessibility-js-url";
54
55     public static final String ACCESSIBILITY_DEBUG_BRAILLE_SERVICE = "debug-braille-service";
56
57     // Sets the ISO country code that will be used for phone number detection.
58     public static final String NETWORK_COUNTRY_ISO = "network-country-iso";
59
60     // Whether to enable the auto-hiding top controls.
61     public static final String ENABLE_TOP_CONTROLS_POSITION_CALCULATION
62             = "enable-top-controls-position-calculation";
63
64     // The height of the movable top controls.
65     public static final String TOP_CONTROLS_HEIGHT = "top-controls-height";
66
67     // How much of the top controls need to be shown before they will auto show.
68     public static final String TOP_CONTROLS_SHOW_THRESHOLD = "top-controls-show-threshold";
69
70     // How much of the top controls need to be hidden before they will auto hide.
71     public static final String TOP_CONTROLS_HIDE_THRESHOLD = "top-controls-hide-threshold";
72
73     // Native switch - chrome_switches::kEnableInstantExtendedAPI
74     public static final String ENABLE_INSTANT_EXTENDED_API = "enable-instant-extended-api";
75
76     // Native switch - content_switches::kEnableSpeechRecognition
77     public static final String ENABLE_SPEECH_RECOGNITION = "enable-speech-recognition";
78
79     // Native switch - shell_switches::kDumpRenderTree
80     public static final String DUMP_RENDER_TREE = "dump-render-tree";
81
82     // Native switch - chrome_switches::kDisablePopupBlocking
83     public static final String DISABLE_POPUP_BLOCKING = "disable-popup-blocking";
84
85     // Whether to disable the click delay by sending click events during double tap
86     public static final String DISABLE_CLICK_DELAY = "disable-click-delay";
87
88     // Public abstract interface, implemented in derived classes.
89     // All these methods reflect their native-side counterparts.
90     /**
91      *  Returns true if this command line contains the given switch.
92      *  (Switch names ARE case-sensitive).
93      */
94     public abstract boolean hasSwitch(String switchString);
95
96     /**
97      * Return the value associated with the given switch, or null.
98      * @param switchString The switch key to lookup. It should NOT start with '--' !
99      * @return switch value, or null if the switch is not set or set to empty.
100      */
101     public abstract String getSwitchValue(String switchString);
102
103     /**
104      * Return the value associated with the given switch, or {@code defaultValue} if the switch
105      * was not specified.
106      * @param switchString The switch key to lookup. It should NOT start with '--' !
107      * @param defaultValue The default value to return if the switch isn't set.
108      * @return Switch value, or {@code defaultValue} if the switch is not set or set to empty.
109      */
110     public String getSwitchValue(String switchString, String defaultValue) {
111         String value = getSwitchValue(switchString);
112         return TextUtils.isEmpty(value) ? defaultValue : value;
113     }
114
115     /**
116      * Append a switch to the command line.  There is no guarantee
117      * this action happens before the switch is needed.
118      * @param switchString the switch to add.  It should NOT start with '--' !
119      */
120     public abstract void appendSwitch(String switchString);
121
122     /**
123      * Append a switch and value to the command line.  There is no
124      * guarantee this action happens before the switch is needed.
125      * @param switchString the switch to add.  It should NOT start with '--' !
126      * @param value the value for this switch.
127      * For example, --foo=bar becomes 'foo', 'bar'.
128      */
129     public abstract void appendSwitchWithValue(String switchString, String value);
130
131     /**
132      * Append switch/value items in "command line" format (excluding argv[0] program name).
133      * E.g. { '--gofast', '--username=fred' }
134      * @param array an array of switch or switch/value items in command line format.
135      *   Unlike the other append routines, these switches SHOULD start with '--' .
136      *   Unlike init(), this does not include the program name in array[0].
137      */
138     public abstract void appendSwitchesAndArguments(String[] array);
139
140     /**
141      * Determine if the command line is bound to the native (JNI) implementation.
142      * @return true if the underlying implementation is delegating to the native command line.
143      */
144     public boolean isNativeImplementation() {
145         return false;
146     }
147
148     private static final AtomicReference<CommandLine> sCommandLine =
149         new AtomicReference<CommandLine>();
150
151     /**
152      * @returns true if the command line has already been initialized.
153      */
154     public static boolean isInitialized() {
155         return sCommandLine.get() != null;
156     }
157
158     // Equivalent to CommandLine::ForCurrentProcess in C++.
159     public static CommandLine getInstance() {
160         CommandLine commandLine = sCommandLine.get();
161         assert commandLine != null;
162         return commandLine;
163     }
164
165     /**
166      * Initialize the singleton instance, must be called exactly once (either directly or
167      * via one of the convenience wrappers below) before using the static singleton instance.
168      * @param args command line flags in 'argv' format: args[0] is the program name.
169      */
170     public static void init(String[] args) {
171         setInstance(new JavaCommandLine(args));
172     }
173
174     /**
175      * Initialize the command line from the command-line file.
176      *
177      * @param file The fully qualified command line file.
178      */
179     public static void initFromFile(String file) {
180         // Arbitrary clamp of 8k on the amount of file we read in.
181         char[] buffer = readUtf8FileFully(file, 8 * 1024);
182         init(buffer == null ? null : tokenizeQuotedAruments(buffer));
183     }
184
185     /**
186      * Resets both the java proxy and the native command lines. This allows the entire
187      * command line initialization to be re-run including the call to onJniLoaded.
188      */
189     public static void reset() {
190         setInstance(null);
191     }
192
193     /**
194      * Public for testing (TODO: why are the tests in a different package?)
195      * Parse command line flags from a flat buffer, supporting double-quote enclosed strings
196      * containing whitespace. argv elements are derived by splitting the buffer on whitepace;
197      * double quote characters may enclose tokens containing whitespace; a double-quote literal
198      * may be escaped with back-slash. (Otherwise backslash is taken as a literal).
199      * @param buffer A command line in command line file format as described above.
200      * @return the tokenized arguments, suitable for passing to init().
201      */
202     public static String[] tokenizeQuotedAruments(char[] buffer) {
203         ArrayList<String> args = new ArrayList<String>();
204         StringBuilder arg = null;
205         final char noQuote = '\0';
206         final char singleQuote = '\'';
207         final char doubleQuote = '"';
208         char currentQuote = noQuote;
209         for (char c : buffer) {
210             // Detect start or end of quote block.
211             if ((currentQuote == noQuote && (c == singleQuote || c == doubleQuote)) ||
212                 c == currentQuote) {
213                 if (arg != null && arg.length() > 0 && arg.charAt(arg.length() - 1) == '\\') {
214                     // Last char was a backslash; pop it, and treat c as a literal.
215                     arg.setCharAt(arg.length() - 1, c);
216                 } else {
217                     currentQuote = currentQuote == noQuote ? c : noQuote;
218                 }
219             } else if (currentQuote == noQuote && Character.isWhitespace(c)) {
220                 if (arg != null) {
221                     args.add(arg.toString());
222                     arg = null;
223                 }
224             } else {
225                 if (arg == null) arg = new StringBuilder();
226                 arg.append(c);
227             }
228         }
229         if (arg != null) {
230             if (currentQuote != noQuote) {
231                 Log.w(TAG, "Unterminated quoted string: " + arg);
232             }
233             args.add(arg.toString());
234         }
235         return args.toArray(new String[args.size()]);
236     }
237
238     private static final String TAG = "CommandLine";
239     private static final String SWITCH_PREFIX = "--";
240     private static final String SWITCH_TERMINATOR = SWITCH_PREFIX;
241     private static final String SWITCH_VALUE_SEPARATOR = "=";
242
243     public static void enableNativeProxy() {
244         // Make a best-effort to ensure we make a clean (atomic) switch over from the old to
245         // the new command line implementation. If another thread is modifying the command line
246         // when this happens, all bets are off. (As per the native CommandLine).
247         sCommandLine.set(new NativeCommandLine());
248     }
249
250     public static String[] getJavaSwitchesOrNull() {
251         CommandLine commandLine = sCommandLine.get();
252         if (commandLine != null) {
253             assert !commandLine.isNativeImplementation();
254             return ((JavaCommandLine) commandLine).getCommandLineArguments();
255         }
256         return null;
257     }
258
259     private static void setInstance(CommandLine commandLine) {
260         CommandLine oldCommandLine = sCommandLine.getAndSet(commandLine);
261         if (oldCommandLine != null && oldCommandLine.isNativeImplementation()) {
262             nativeReset();
263         }
264     }
265
266     /**
267      * @param fileName the file to read in.
268      * @param sizeLimit cap on the file size.
269      * @return Array of chars read from the file, or null if the file cannot be read
270      *         or if its length exceeds |sizeLimit|.
271      */
272     private static char[] readUtf8FileFully(String fileName, int sizeLimit) {
273         Reader reader = null;
274         File f = new File(fileName);
275         long fileLength = f.length();
276
277         if (fileLength == 0) {
278             return null;
279         }
280
281         if (fileLength > sizeLimit) {
282             Log.w(TAG, "File " + fileName + " length " + fileLength + " exceeds limit "
283                     + sizeLimit);
284             return null;
285         }
286
287         try {
288             char[] buffer = new char[(int) fileLength];
289             reader = new InputStreamReader(new FileInputStream(f), "UTF-8");
290             int charsRead = reader.read(buffer);
291             // Debug check that we've exhausted the input stream (will fail e.g. if the
292             // file grew after we inspected its length).
293             assert !reader.ready();
294             return charsRead < buffer.length ? Arrays.copyOfRange(buffer, 0, charsRead) : buffer;
295         } catch (FileNotFoundException e) {
296             return null;
297         } catch (IOException e) {
298             return null;
299         } finally {
300             try {
301                 if (reader != null) reader.close();
302             } catch (IOException e) {
303                 Log.e(TAG, "Unable to close file reader.", e);
304             }
305         }
306     }
307
308     private CommandLine() {}
309
310     private static class JavaCommandLine extends CommandLine {
311         private HashMap<String, String> mSwitches = new HashMap<String, String>();
312         private ArrayList<String> mArgs = new ArrayList<String>();
313
314         // The arguments begin at index 1, since index 0 contains the executable name.
315         private int mArgsBegin = 1;
316
317         JavaCommandLine(String[] args) {
318             if (args == null || args.length == 0 || args[0] == null) {
319                 mArgs.add("");
320             } else {
321                 mArgs.add(args[0]);
322                 appendSwitchesInternal(args, 1);
323             }
324             // Invariant: we always have the argv[0] program name element.
325             assert mArgs.size() > 0;
326         }
327
328         /**
329          * Returns the switches and arguments passed into the program, with switches and their
330          * values coming before all of the arguments.
331          */
332         private String[] getCommandLineArguments() {
333             return mArgs.toArray(new String[mArgs.size()]);
334         }
335
336         @Override
337         public boolean hasSwitch(String switchString) {
338             return mSwitches.containsKey(switchString);
339         }
340
341         @Override
342         public String getSwitchValue(String switchString) {
343             // This is slightly round about, but needed for consistency with the NativeCommandLine
344             // version which does not distinguish empty values from key not present.
345             String value = mSwitches.get(switchString);
346             return value == null || value.isEmpty() ? null : value;
347         }
348
349         @Override
350         public void appendSwitch(String switchString) {
351             appendSwitchWithValue(switchString, null);
352         }
353
354         /**
355          * Appends a switch to the current list.
356          * @param switchString the switch to add.  It should NOT start with '--' !
357          * @param value the value for this switch.
358          */
359         @Override
360         public void appendSwitchWithValue(String switchString, String value) {
361             mSwitches.put(switchString, value == null ? "" : value);
362
363             // Append the switch and update the switches/arguments divider mArgsBegin.
364             String combinedSwitchString = SWITCH_PREFIX + switchString;
365             if (value != null && !value.isEmpty())
366                 combinedSwitchString += SWITCH_VALUE_SEPARATOR + value;
367
368             mArgs.add(mArgsBegin++, combinedSwitchString);
369         }
370
371         @Override
372         public void appendSwitchesAndArguments(String[] array) {
373             appendSwitchesInternal(array, 0);
374         }
375
376         // Add the specified arguments, but skipping the first |skipCount| elements.
377         private void appendSwitchesInternal(String[] array, int skipCount) {
378             boolean parseSwitches = true;
379             for (String arg : array) {
380                 if (skipCount > 0) {
381                     --skipCount;
382                     continue;
383                 }
384
385                 if (arg.equals(SWITCH_TERMINATOR)) {
386                     parseSwitches = false;
387                 }
388
389                 if (parseSwitches && arg.startsWith(SWITCH_PREFIX)) {
390                     String[] parts = arg.split(SWITCH_VALUE_SEPARATOR, 2);
391                     String value = parts.length > 1 ? parts[1] : null;
392                     appendSwitchWithValue(parts[0].substring(SWITCH_PREFIX.length()), value);
393                 } else {
394                     mArgs.add(arg);
395                 }
396             }
397         }
398     }
399
400     private static class NativeCommandLine extends CommandLine {
401         @Override
402         public boolean hasSwitch(String switchString) {
403             return nativeHasSwitch(switchString);
404         }
405
406         @Override
407         public String getSwitchValue(String switchString) {
408             return nativeGetSwitchValue(switchString);
409         }
410
411         @Override
412         public void appendSwitch(String switchString) {
413             nativeAppendSwitch(switchString);
414         }
415
416         @Override
417         public void appendSwitchWithValue(String switchString, String value) {
418             nativeAppendSwitchWithValue(switchString, value);
419         }
420
421         @Override
422         public void appendSwitchesAndArguments(String[] array) {
423             nativeAppendSwitchesAndArguments(array);
424         }
425
426         @Override
427         public boolean isNativeImplementation() {
428             return true;
429         }
430     }
431
432     private static native void nativeReset();
433     private static native boolean nativeHasSwitch(String switchString);
434     private static native String nativeGetSwitchValue(String switchString);
435     private static native void nativeAppendSwitch(String switchString);
436     private static native void nativeAppendSwitchWithValue(String switchString, String value);
437     private static native void nativeAppendSwitchesAndArguments(String[] array);
438 };