ContentShellActivity.java revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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
5package org.chromium.content_shell_apk;
6
7import android.app.Activity;
8import android.content.BroadcastReceiver;
9import android.content.Context;
10import android.content.Intent;
11import android.content.IntentFilter;
12import android.os.Bundle;
13import android.text.TextUtils;
14import android.util.Log;
15import android.view.KeyEvent;
16import android.widget.Toast;
17
18import org.chromium.base.MemoryPressureListener;
19import org.chromium.content.app.LibraryLoader;
20import org.chromium.content.browser.ActivityContentVideoViewClient;
21import org.chromium.content.browser.BrowserStartupController;
22import org.chromium.content.browser.ContentVideoViewClient;
23import org.chromium.content.browser.ContentView;
24import org.chromium.content.browser.ContentViewClient;
25import org.chromium.content.browser.DeviceUtils;
26import org.chromium.content.browser.TracingIntentHandler;
27import org.chromium.content.common.CommandLine;
28import org.chromium.content.common.ProcessInitException;
29import org.chromium.content_shell.Shell;
30import org.chromium.content_shell.ShellManager;
31import org.chromium.ui.WindowAndroid;
32
33/**
34 * Activity for managing the Content Shell.
35 */
36public class ContentShellActivity extends Activity {
37
38    public static final String COMMAND_LINE_FILE = "/data/local/tmp/content-shell-command-line";
39    private static final String TAG = "ContentShellActivity";
40
41    private static final String ACTIVE_SHELL_URL_KEY = "activeUrl";
42    private static final String ACTION_START_TRACE =
43            "org.chromium.content_shell.action.PROFILE_START";
44    private static final String ACTION_STOP_TRACE =
45            "org.chromium.content_shell.action.PROFILE_STOP";
46    public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs";
47
48    /**
49     * Sending an intent with this action will simulate a memory pressure signal at a critical
50     * level.
51     */
52    private static final String ACTION_LOW_MEMORY =
53            "org.chromium.content_shell.action.ACTION_LOW_MEMORY";
54
55    /**
56     * Sending an intent with this action will simulate a memory pressure signal at a moderate
57     * level.
58     */
59    private static final String ACTION_TRIM_MEMORY_MODERATE =
60            "org.chromium.content_shell.action.ACTION_TRIM_MEMORY_MODERATE";
61
62
63    private ShellManager mShellManager;
64    private WindowAndroid mWindowAndroid;
65    private BroadcastReceiver mReceiver;
66
67    @Override
68    protected void onCreate(final Bundle savedInstanceState) {
69        super.onCreate(savedInstanceState);
70
71        // Initializing the command line must occur before loading the library.
72        if (!CommandLine.isInitialized()) {
73            CommandLine.initFromFile(COMMAND_LINE_FILE);
74            String[] commandLineParams = getCommandLineParamsFromIntent(getIntent());
75            if (commandLineParams != null) {
76                CommandLine.getInstance().appendSwitchesAndArguments(commandLineParams);
77            }
78        }
79        waitForDebuggerIfNeeded();
80
81        DeviceUtils.addDeviceSpecificUserAgentSwitch(this);
82        try {
83            LibraryLoader.ensureInitialized();
84        } catch (ProcessInitException e) {
85            Log.e(TAG, "ContentView initialization failed.", e);
86            finish();
87            return;
88        }
89
90        setContentView(R.layout.content_shell_activity);
91        mShellManager = (ShellManager) findViewById(R.id.shell_container);
92        mWindowAndroid = new WindowAndroid(this);
93        mWindowAndroid.restoreInstanceState(savedInstanceState);
94        mShellManager.setWindow(mWindowAndroid);
95
96        String startupUrl = getUrlFromIntent(getIntent());
97        if (!TextUtils.isEmpty(startupUrl)) {
98            mShellManager.setStartupUrl(Shell.sanitizeUrl(startupUrl));
99        }
100
101        if (CommandLine.getInstance().hasSwitch(CommandLine.DUMP_RENDER_TREE)) {
102            if(BrowserStartupController.get(this).startBrowserProcessesSync(
103                   BrowserStartupController.MAX_RENDERERS_LIMIT)) {
104                finishInitialization(savedInstanceState);
105            } else {
106                initializationFailed();
107            }
108        } else {
109            BrowserStartupController.get(this).startBrowserProcessesAsync(
110                    new BrowserStartupController.StartupCallback() {
111                @Override
112                public void onSuccess(boolean alreadyStarted) {
113                    finishInitialization(savedInstanceState);
114                }
115
116                @Override
117                public void onFailure() {
118                    initializationFailed();
119                }
120            });
121        }
122    }
123
124    private void finishInitialization(Bundle savedInstanceState) {
125        String shellUrl = ShellManager.DEFAULT_SHELL_URL;
126        if (savedInstanceState != null
127                && savedInstanceState.containsKey(ACTIVE_SHELL_URL_KEY)) {
128            shellUrl = savedInstanceState.getString(ACTIVE_SHELL_URL_KEY);
129        }
130        mShellManager.launchShell(shellUrl);
131        getActiveContentView().setContentViewClient(new ContentViewClient() {
132            @Override
133            public ContentVideoViewClient getContentVideoViewClient() {
134                return new ActivityContentVideoViewClient(ContentShellActivity.this);
135            }
136        });
137    }
138
139    private void initializationFailed() {
140        Log.e(TAG, "ContentView initialization failed.");
141        Toast.makeText(ContentShellActivity.this,
142                R.string.browser_process_initialization_failed,
143                Toast.LENGTH_SHORT).show();
144        finish();
145    }
146
147    @Override
148    protected void onSaveInstanceState(Bundle outState) {
149        super.onSaveInstanceState(outState);
150        Shell activeShell = getActiveShell();
151        if (activeShell != null) {
152            outState.putString(ACTIVE_SHELL_URL_KEY, activeShell.getContentView().getUrl());
153        }
154
155        mWindowAndroid.saveInstanceState(outState);
156    }
157
158    private void waitForDebuggerIfNeeded() {
159        if (CommandLine.getInstance().hasSwitch(CommandLine.WAIT_FOR_JAVA_DEBUGGER)) {
160            Log.e(TAG, "Waiting for Java debugger to connect...");
161            android.os.Debug.waitForDebugger();
162            Log.e(TAG, "Java debugger connected. Resuming execution.");
163        }
164    }
165
166    @Override
167    public boolean onKeyUp(int keyCode, KeyEvent event) {
168        if (keyCode != KeyEvent.KEYCODE_BACK) return super.onKeyUp(keyCode, event);
169
170        Shell activeView = getActiveShell();
171        if (activeView != null && activeView.getContentView().canGoBack()) {
172            activeView.getContentView().goBack();
173            return true;
174        }
175
176        return super.onKeyUp(keyCode, event);
177    }
178
179    @Override
180    protected void onNewIntent(Intent intent) {
181        if (getCommandLineParamsFromIntent(intent) != null) {
182            Log.i(TAG, "Ignoring command line params: can only be set when creating the activity.");
183        }
184
185        if (ACTION_LOW_MEMORY.equals(intent.getAction())) {
186            MemoryPressureListener.simulateMemoryPressureSignal(TRIM_MEMORY_COMPLETE);
187            return;
188        } else if (ACTION_TRIM_MEMORY_MODERATE.equals(intent.getAction())) {
189            MemoryPressureListener.simulateMemoryPressureSignal(TRIM_MEMORY_MODERATE);
190            return;
191        }
192
193        String url = getUrlFromIntent(intent);
194        if (!TextUtils.isEmpty(url)) {
195            Shell activeView = getActiveShell();
196            if (activeView != null) {
197                activeView.loadUrl(url);
198            }
199        }
200    }
201
202    @Override
203    protected void onPause() {
204        ContentView view = getActiveContentView();
205        if (view != null) view.onActivityPause();
206
207        super.onPause();
208        unregisterReceiver(mReceiver);
209    }
210
211    @Override
212    protected void onResume() {
213        super.onResume();
214
215        ContentView view = getActiveContentView();
216        if (view != null) view.onActivityResume();
217        IntentFilter intentFilter = new IntentFilter(ACTION_START_TRACE);
218        intentFilter.addAction(ACTION_STOP_TRACE);
219        mReceiver = new BroadcastReceiver() {
220            @Override
221            public void onReceive(Context context, Intent intent) {
222                String action = intent.getAction();
223                String extra = intent.getStringExtra("file");
224                if (ACTION_START_TRACE.equals(action)) {
225                    if (extra.isEmpty()) {
226                        Log.e(TAG, "Can not start tracing without specifing saving location");
227                    } else {
228                        TracingIntentHandler.beginTracing(extra);
229                        Log.i(TAG, "start tracing");
230                    }
231                } else if (ACTION_STOP_TRACE.equals(action)) {
232                    Log.i(TAG, "stop tracing");
233                    TracingIntentHandler.endTracing();
234                }
235            }
236        };
237        registerReceiver(mReceiver, intentFilter);
238    }
239
240    @Override
241    public void onActivityResult(int requestCode, int resultCode, Intent data) {
242        super.onActivityResult(requestCode, resultCode, data);
243        mWindowAndroid.onActivityResult(requestCode, resultCode, data);
244    }
245
246    private static String getUrlFromIntent(Intent intent) {
247        return intent != null ? intent.getDataString() : null;
248    }
249
250    private static String[] getCommandLineParamsFromIntent(Intent intent) {
251        return intent != null ? intent.getStringArrayExtra(COMMAND_LINE_ARGS_KEY) : null;
252    }
253
254    /**
255     * @return The {@link ShellManager} configured for the activity or null if it has not been
256     *         created yet.
257     */
258    public ShellManager getShellManager() {
259        return mShellManager;
260    }
261
262    /**
263     * @return The currently visible {@link Shell} or null if one is not showing.
264     */
265    public Shell getActiveShell() {
266        return mShellManager != null ? mShellManager.getActiveShell() : null;
267    }
268
269    /**
270     * @return The {@link ContentView} owned by the currently visible {@link Shell} or null if one
271     *         is not showing.
272     */
273    public ContentView getActiveContentView() {
274        Shell shell = getActiveShell();
275        return shell != null ? shell.getContentView() : null;
276    }
277}
278