EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.content.browser]

COVERAGE SUMMARY FOR SOURCE FILE [BrowserStartupController.java]

nameclass, %method, %block, %line, %
BrowserStartupController.java100% (3/3)100% (21/21)89%  (237/265)95%  (65.4/69)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class BrowserStartupController100% (1/1)100% (17/17)87%  (195/223)94%  (59.4/63)
addStartupCompletedObserver (BrowserStartupController$StartupCallback): void 100% (1/1)64%  (9/14)80%  (4/5)
get (Context): BrowserStartupController 100% (1/1)75%  (15/20)89%  (4.4/5)
initializeAndroidBrowserProcess (): boolean 100% (1/1)78%  (7/9)77%  (0.8/1)
<static initializer> 100% (1/1)80%  (8/10)90%  (1.8/2)
startBrowserProcessesAsync (BrowserStartupController$StartupCallback): void 100% (1/1)84%  (27/32)94%  (9.4/10)
executeEnqueuedCallbacks (int, boolean): void 100% (1/1)87%  (34/39)94%  (8.4/9)
tryToInitializeBrowserProcess (): void 100% (1/1)89%  (33/37)96%  (10.6/11)
BrowserStartupController (Context): void 100% (1/1)100% (11/11)100% (4/4)
access$000 (BrowserStartupController, int, boolean): void 100% (1/1)100% (5/5)100% (1/1)
access$100 (BrowserStartupController): boolean 100% (1/1)100% (3/3)100% (1/1)
browserMayStartAsynchonously (): boolean 100% (1/1)100% (2/2)100% (1/1)
browserStartupComplete (int): void 100% (1/1)100% (7/7)100% (3/3)
enableAsynchronousStartup (): void 100% (1/1)100% (2/2)100% (2/2)
enqueueCallbackExecution (int, boolean): void 100% (1/1)100% (12/12)100% (2/2)
overrideInstanceForTest (BrowserStartupController): BrowserStartupController 100% (1/1)100% (6/6)100% (3/3)
postStartupCompleted (BrowserStartupController$StartupCallback): void 100% (1/1)100% (11/11)100% (2/2)
setAsynchronousStartupConfig (): void 100% (1/1)100% (3/3)100% (2/2)
     
class BrowserStartupController$1100% (1/1)100% (2/2)100% (20/20)100% (3/3)
BrowserStartupController$1 (BrowserStartupController, int, boolean): void 100% (1/1)100% (12/12)100% (1/1)
run (): void 100% (1/1)100% (8/8)100% (2/2)
     
class BrowserStartupController$2100% (1/1)100% (2/2)100% (22/22)100% (5/5)
BrowserStartupController$2 (BrowserStartupController, BrowserStartupControlle... 100% (1/1)100% (9/9)100% (1/1)
run (): void 100% (1/1)100% (13/13)100% (4/4)

1// Copyright 2013 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.browser;
6 
7import android.content.Context;
8import android.os.Handler;
9import android.util.Log;
10 
11import com.google.common.annotations.VisibleForTesting;
12 
13import org.chromium.base.CalledByNative;
14import org.chromium.base.JNINamespace;
15import org.chromium.base.ThreadUtils;
16import org.chromium.content.common.ProcessInitException;
17 
18import java.util.ArrayList;
19import java.util.List;
20 
21/**
22 * This class controls how C++ browser main loop is started and ensures it happens only once.
23 *
24 * It supports kicking off the startup sequence in an asynchronous way. Startup can be called as
25 * many times as needed (for instance, multiple activities for the same application), but the
26 * browser process will still only be initialized once. All requests to start the browser will
27 * always get their callback executed; if the browser process has already been started, the callback
28 * is called immediately, else it is called when initialization is complete.
29 *
30 * All communication with this class must happen on the main thread.
31 *
32 * This is a singleton, and stores a reference to the application context.
33 */
34@JNINamespace("content")
35public class BrowserStartupController {
36 
37    public interface StartupCallback {
38        void onSuccess(boolean alreadyStarted);
39        void onFailure();
40    }
41 
42    private static final String TAG = "BrowserStartupController";
43 
44    // Helper constants for {@link StartupCallback#onSuccess}.
45    private static final boolean ALREADY_STARTED = true;
46    private static final boolean NOT_ALREADY_STARTED = false;
47 
48    // Helper constants for {@link #executeEnqueuedCallbacks(int, boolean)}.
49    @VisibleForTesting
50    static final int STARTUP_SUCCESS = -1;
51    @VisibleForTesting
52    static final int STARTUP_FAILURE = 1;
53 
54    private static BrowserStartupController sInstance;
55 
56    private static boolean sBrowserMayStartAsynchronously = false;
57 
58    private static void setAsynchronousStartupConfig() {
59        sBrowserMayStartAsynchronously = true;
60    }
61 
62    @CalledByNative
63    private static boolean browserMayStartAsynchonously() {
64        return sBrowserMayStartAsynchronously;
65    }
66 
67    @VisibleForTesting
68    @CalledByNative
69    static void browserStartupComplete(int result) {
70        if (sInstance != null) {
71            sInstance.executeEnqueuedCallbacks(result, NOT_ALREADY_STARTED);
72        }
73    }
74 
75    // A list of callbacks that should be called when the async startup of the browser process is
76    // complete.
77    private final List<StartupCallback> mAsyncStartupCallbacks;
78 
79    // The context is set on creation, but the reference is cleared after the browser process
80    // initialization has been started, since it is not needed anymore. This is to ensure the
81    // context is not leaked.
82    private Context mContext;
83 
84    // Whether the async startup of the browser process has started.
85    private boolean mHasStartedInitializingBrowserProcess;
86 
87    // Whether the async startup of the browser process is complete.
88    private boolean mAsyncStartupDone;
89 
90    // This field is set after startup has been completed based on whether the startup was a success
91    // or not. It is used when later requests to startup come in that happen after the initial set
92    // of enqueued callbacks have been executed.
93    private boolean mStartupSuccess;
94 
95    BrowserStartupController(Context context) {
96        mContext = context;
97        mAsyncStartupCallbacks = new ArrayList<StartupCallback>();
98    }
99 
100    public static BrowserStartupController get(Context context) {
101        assert ThreadUtils.runningOnUiThread() : "Tried to start the browser on the wrong thread.";
102        ThreadUtils.assertOnUiThread();
103        if (sInstance == null) {
104            sInstance = new BrowserStartupController(context.getApplicationContext());
105        }
106        return sInstance;
107    }
108 
109    @VisibleForTesting
110    static BrowserStartupController overrideInstanceForTest(BrowserStartupController controller) {
111        if (sInstance == null) {
112            sInstance = controller;
113        }
114        return sInstance;
115    }
116 
117    /**
118     * Start the browser process asynchronously. This will set up a queue of UI thread tasks to
119     * initialize the browser process.
120     * <p/>
121     * Note that this can only be called on the UI thread.
122     *
123     * @param callback the callback to be called when browser startup is complete.
124     */
125    public void startBrowserProcessesAsync(final StartupCallback callback) {
126        assert ThreadUtils.runningOnUiThread() : "Tried to start the browser on the wrong thread.";
127        if (mAsyncStartupDone) {
128            // Browser process initialization has already been completed, so we can immediately post
129            // the callback.
130            postStartupCompleted(callback);
131            return;
132        }
133 
134        // Browser process has not been fully started yet, so we defer executing the callback.
135        mAsyncStartupCallbacks.add(callback);
136 
137        if (!mHasStartedInitializingBrowserProcess) {
138            // This is the first time we have been asked to start the browser process. We set the
139            // flag that indicates that we have kicked off starting the browser process.
140            mHasStartedInitializingBrowserProcess = true;
141 
142            enableAsynchronousStartup();
143 
144            // Try to initialize the Android browser process.
145            tryToInitializeBrowserProcess();
146        }
147    }
148 
149    private void tryToInitializeBrowserProcess() {
150        try {
151            assert mContext != null;
152            boolean wasAlreadyInitialized = initializeAndroidBrowserProcess();
153            // The context is not needed anymore, so clear the member field to not leak.
154            mContext = null;
155            if (wasAlreadyInitialized) {
156                // Something has already initialized the browser process before we got to setup the
157                // async startup. This means that we will never get a callback, so manually call
158                // them now, and just assume that the startup was successful.
159                Log.w(TAG, "Browser process was initialized without BrowserStartupController");
160                enqueueCallbackExecution(STARTUP_SUCCESS, ALREADY_STARTED);
161            }
162        } catch (ProcessInitException e) {
163            Log.e(TAG, "Unable to start browser process.", e);
164            // ProcessInitException could mean one of two things:
165            // 1) The LibraryLoader failed.
166            // 2) ContentMain failed to start.
167            // It is unclear whether the browser tasks have already been started, and in case they
168            // have not, post a message to execute all the callbacks. Whichever call to
169            // executeEnqueuedCallbacks comes first will trigger the callbacks, but since the list
170            // of callbacks is then cleared, they will only be called once.
171            enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
172        }
173    }
174 
175    public void addStartupCompletedObserver(StartupCallback callback) {
176        ThreadUtils.assertOnUiThread();
177        if (mAsyncStartupDone)
178            postStartupCompleted(callback);
179        else
180            mAsyncStartupCallbacks.add(callback);
181    }
182 
183    private void executeEnqueuedCallbacks(int startupResult, boolean alreadyStarted) {
184        assert ThreadUtils.runningOnUiThread() : "Callback from browser startup from wrong thread.";
185        mAsyncStartupDone = true;
186        for (StartupCallback asyncStartupCallback : mAsyncStartupCallbacks) {
187            if (startupResult > 0) {
188                asyncStartupCallback.onFailure();
189            } else {
190                mStartupSuccess = true;
191                asyncStartupCallback.onSuccess(alreadyStarted);
192            }
193        }
194        // We don't want to hold on to any objects after we do not need them anymore.
195        mAsyncStartupCallbacks.clear();
196    }
197 
198    private void enqueueCallbackExecution(final int startupFailure, final boolean alreadyStarted) {
199        new Handler().post(new Runnable() {
200            @Override
201            public void run() {
202                executeEnqueuedCallbacks(startupFailure, alreadyStarted);
203            }
204        });
205    }
206 
207    private void postStartupCompleted(final StartupCallback callback) {
208        new Handler().post(new Runnable() {
209            @Override
210            public void run() {
211                if (mStartupSuccess)
212                    callback.onSuccess(ALREADY_STARTED);
213                else
214                    callback.onFailure();
215            }
216        });
217    }
218 
219    /**
220     * Ensure that the browser process will be asynchronously started up. This also ensures that we
221     * get a call to {@link #browserStartupComplete} when the browser startup is complete.
222     */
223    @VisibleForTesting
224    void enableAsynchronousStartup() {
225        setAsynchronousStartupConfig();
226    }
227 
228    /**
229     * @return whether the process was already initialized, so native was not instructed to start.
230     */
231    @VisibleForTesting
232    boolean initializeAndroidBrowserProcess() throws ProcessInitException {
233        return !AndroidBrowserProcess.init(mContext, AndroidBrowserProcess.MAX_RENDERERS_LIMIT);
234    }
235}

[all classes][org.chromium.content.browser]
EMMA 2.0.5312 (C) Vladimir Roubtsov