summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoraberent@chromium.org <aberent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-27 15:29:56 +0000
committeraberent@chromium.org <aberent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-27 15:29:56 +0000
commit232e09d18fa339b9dcb49f30d2e919fe673da1c5 (patch)
treed1a7e8698c6a7fab8f3f7925a81760f18504d5f7
parentdc0fd43f60f29a9167f90cf1559ce1adc17d9969 (diff)
downloadchromium_src-232e09d18fa339b9dcb49f30d2e919fe673da1c5.zip
chromium_src-232e09d18fa339b9dcb49f30d2e919fe673da1c5.tar.gz
chromium_src-232e09d18fa339b9dcb49f30d2e919fe673da1c5.tar.bz2
Allow overlapping sync and async startup requests
On Android we can get a second request to start the browser while the an asynchronous request is in progress. Since the second request may be synchronous, we may have switch to completing initialization synchronously. This patch handles this by tracking which initialization tasks have been run, and running the remaining initialization tasks. BUG=260574 Review URL: https://chromiumcodereview.appspot.com/22691002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219795 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java12
-rw-r--r--chrome/app/android/chrome_main_delegate_android.cc8
-rw-r--r--content/app/android/content_main.cc13
-rw-r--r--content/browser/android/android_browser_process.cc47
-rw-r--r--content/browser/android/android_browser_process.h16
-rw-r--r--content/browser/android/browser_jni_registrar.cc2
-rw-r--r--content/browser/android/browser_startup_controller.cc31
-rw-r--r--content/browser/browser_main_loop.cc61
-rw-r--r--content/browser/browser_main_loop.h8
-rw-r--r--content/browser/browser_main_runner.cc101
-rw-r--r--content/browser/startup_task_runner.cc50
-rw-r--r--content/browser/startup_task_runner.h20
-rw-r--r--content/browser/startup_task_runner_unittest.cc45
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/content_jni.gypi1
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java104
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java180
-rw-r--r--content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java168
-rw-r--r--content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java6
-rw-r--r--content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java18
20 files changed, 486 insertions, 407 deletions
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
index 7c3ab76..c0657a3 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
@@ -5,12 +5,11 @@
package org.chromium.android_webview;
import android.content.Context;
-import android.content.SharedPreferences;
import org.chromium.base.PathUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.content.app.LibraryLoader;
-import org.chromium.content.browser.AndroidBrowserProcess;
+import org.chromium.content.browser.BrowserStartupController;
import org.chromium.content.common.ProcessInitException;
/**
@@ -46,12 +45,9 @@ public abstract class AwBrowserProcess {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
- try {
- LibraryLoader.ensureInitialized();
- AndroidBrowserProcess.init(context,
- AndroidBrowserProcess.MAX_RENDERERS_SINGLE_PROCESS);
- } catch (ProcessInitException e) {
- throw new RuntimeException("Cannot initialize WebView", e);
+ if( !BrowserStartupController.get(context).startBrowserProcessesSync(
+ BrowserStartupController.MAX_RENDERERS_SINGLE_PROCESS)) {
+ throw new RuntimeException("Cannot initialize WebView");
}
}
});
diff --git a/chrome/app/android/chrome_main_delegate_android.cc b/chrome/app/android/chrome_main_delegate_android.cc
index 81f4029..6b726c6 100644
--- a/chrome/app/android/chrome_main_delegate_android.cc
+++ b/chrome/app/android/chrome_main_delegate_android.cc
@@ -32,7 +32,13 @@ int ChromeMainDelegateAndroid::RunProcess(
JNIEnv* env = base::android::AttachCurrentThread();
RegisterApplicationNativeMethods(env);
- browser_runner_.reset(content::BrowserMainRunner::Create());
+ // Because the browser process can be started asynchronously as a series of
+ // of UI thread tasks a second request to start it can come in while the
+ // first request is still being processed. We must keep the same
+ // browser runner for the second request.
+ if (!browser_runner_.get()) {
+ browser_runner_.reset(content::BrowserMainRunner::Create());
+ }
return browser_runner_->Initialize(main_function_params);
}
diff --git a/content/app/android/content_main.cc b/content/app/android/content_main.cc
index d89d4f2..bfc372f 100644
--- a/content/app/android/content_main.cc
+++ b/content/app/android/content_main.cc
@@ -38,10 +38,15 @@ static void InitApplicationContext(JNIEnv* env, jclass clazz, jobject context) {
static jint Start(JNIEnv* env, jclass clazz) {
TRACE_EVENT0("startup", "content::Start");
- DCHECK(!g_content_runner.Get().get());
- g_content_runner.Get().reset(ContentMainRunner::Create());
- g_content_runner.Get()->Initialize(0, NULL,
- g_content_main_delegate.Get().get());
+ // On Android we can have multiple requests to start the browser in process
+ // simultaneously. If we get an asynchonous request followed by a synchronous
+ // request then we have to call this a second time to finish starting the
+ // browser synchronously.
+ if (!g_content_runner.Get().get()) {
+ g_content_runner.Get().reset(ContentMainRunner::Create());
+ g_content_runner.Get()->Initialize(
+ 0, NULL, g_content_main_delegate.Get().get());
+ }
return g_content_runner.Get()->Run();
}
diff --git a/content/browser/android/android_browser_process.cc b/content/browser/android/android_browser_process.cc
deleted file mode 100644
index edc48e6..0000000
--- a/content/browser/android/android_browser_process.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/android/android_browser_process.h"
-
-#include "base/android/jni_string.h"
-#include "base/debug/debugger.h"
-#include "base/logging.h"
-#include "content/browser/android/content_startup_flags.h"
-#include "content/public/common/content_constants.h"
-#include "jni/AndroidBrowserProcess_jni.h"
-
-using base::android::ConvertJavaStringToUTF8;
-
-namespace content {
-
-static void SetCommandLineFlags(JNIEnv*env,
- jclass clazz,
- jint max_render_process_count,
- jstring plugin_descriptor) {
- std::string plugin_str = (plugin_descriptor == NULL ?
- std::string() : ConvertJavaStringToUTF8(env, plugin_descriptor));
- SetContentCommandLineFlags(max_render_process_count, plugin_str);
-}
-
-static jboolean IsOfficialBuild(JNIEnv* env, jclass clazz) {
-#if defined(OFFICIAL_BUILD)
- return true;
-#else
- return false;
-#endif
-}
-
-static jboolean IsPluginEnabled(JNIEnv* env, jclass clazz) {
-#if defined(ENABLE_PLUGINS)
- return true;
-#else
- return false;
-#endif
-}
-
-bool RegisterAndroidBrowserProcess(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
-} // namespace
diff --git a/content/browser/android/android_browser_process.h b/content/browser/android/android_browser_process.h
deleted file mode 100644
index fbd06e8..0000000
--- a/content/browser/android/android_browser_process.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_
-#define CONTENT_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_
-
-#include <jni.h>
-
-namespace content {
-
-bool RegisterAndroidBrowserProcess(JNIEnv* env);
-
-} // namespace content
-
-#endif // CONTENT_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_
diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc
index 5e7940d..4e331c9 100644
--- a/content/browser/android/browser_jni_registrar.cc
+++ b/content/browser/android/browser_jni_registrar.cc
@@ -8,7 +8,6 @@
#include "base/android/jni_registrar.h"
#include "content/browser/accessibility/browser_accessibility_android.h"
#include "content/browser/accessibility/browser_accessibility_manager_android.h"
-#include "content/browser/android/android_browser_process.h"
#include "content/browser/android/browser_startup_controller.h"
#include "content/browser/android/child_process_launcher_android.h"
#include "content/browser/android/content_settings.h"
@@ -39,7 +38,6 @@ namespace {
base::android::RegistrationMethod kContentRegisteredMethods[] = {
{"AndroidLocationApiAdapter",
content::AndroidLocationApiAdapter::RegisterGeolocationService},
- {"AndroidBrowserProcess", content::RegisterAndroidBrowserProcess},
{"BrowserAccessibilityManager",
content::RegisterBrowserAccessibilityManager},
{"BrowserStartupController", content::RegisterBrowserStartupController},
diff --git a/content/browser/android/browser_startup_controller.cc b/content/browser/android/browser_startup_controller.cc
index 6b90ce2..502f198 100644
--- a/content/browser/android/browser_startup_controller.cc
+++ b/content/browser/android/browser_startup_controller.cc
@@ -5,6 +5,9 @@
#include "content/browser/android/browser_startup_controller.h"
#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "content/browser/android/content_startup_flags.h"
+
#include "jni/BrowserStartupController_jni.h"
namespace content {
@@ -22,4 +25,32 @@ void BrowserStartupComplete(int result) {
bool RegisterBrowserStartupController(JNIEnv* env) {
return RegisterNativesImpl(env);
}
+
+static void SetCommandLineFlags(JNIEnv* env,
+ jclass clazz,
+ jint max_render_process_count,
+ jstring plugin_descriptor) {
+ std::string plugin_str =
+ (plugin_descriptor == NULL
+ ? std::string()
+ : base::android::ConvertJavaStringToUTF8(env, plugin_descriptor));
+ SetContentCommandLineFlags(max_render_process_count, plugin_str);
+}
+
+static jboolean IsOfficialBuild(JNIEnv* env, jclass clazz) {
+#if defined(OFFICIAL_BUILD)
+ return true;
+#else
+ return false;
+#endif
+}
+
+static jboolean IsPluginEnabled(JNIEnv* env, jclass clazz) {
+#if defined(ENABLE_PLUGINS)
+ return true;
+#else
+ return false;
+#endif
+}
+
} // namespace content
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 8bf6503..87bec3d 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -527,36 +527,53 @@ int BrowserMainLoop::PreCreateThreads() {
}
void BrowserMainLoop::CreateStartupTasks() {
- TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks")
+ TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks");
+ // First time through, we really want to create all the tasks
+ if (!startup_task_runner_.get()) {
#if defined(OS_ANDROID)
- scoped_refptr<StartupTaskRunner> task_runner =
- new StartupTaskRunner(BrowserMayStartAsynchronously(),
- base::Bind(&BrowserStartupComplete),
- base::MessageLoop::current()->message_loop_proxy());
+ startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner(
+ base::Bind(&BrowserStartupComplete),
+ base::MessageLoop::current()->message_loop_proxy()));
#else
- scoped_refptr<StartupTaskRunner> task_runner =
- new StartupTaskRunner(false,
- base::Callback<void(int)>(),
- base::MessageLoop::current()->message_loop_proxy());
+ startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner(
+ base::Callback<void(int)>(),
+ base::MessageLoop::current()->message_loop_proxy()));
#endif
- StartupTask pre_create_threads =
- base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this));
- task_runner->AddTask(pre_create_threads);
+ StartupTask pre_create_threads =
+ base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this));
+ startup_task_runner_->AddTask(pre_create_threads);
- StartupTask create_threads =
- base::Bind(&BrowserMainLoop::CreateThreads, base::Unretained(this));
- task_runner->AddTask(create_threads);
+ StartupTask create_threads =
+ base::Bind(&BrowserMainLoop::CreateThreads, base::Unretained(this));
+ startup_task_runner_->AddTask(create_threads);
- StartupTask browser_thread_started = base::Bind(
- &BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this));
- task_runner->AddTask(browser_thread_started);
+ StartupTask browser_thread_started = base::Bind(
+ &BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this));
+ startup_task_runner_->AddTask(browser_thread_started);
- StartupTask pre_main_message_loop_run = base::Bind(
- &BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this));
- task_runner->AddTask(pre_main_message_loop_run);
+ StartupTask pre_main_message_loop_run = base::Bind(
+ &BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this));
+ startup_task_runner_->AddTask(pre_main_message_loop_run);
- task_runner->StartRunningTasks();
+#if defined(OS_ANDROID)
+ if (BrowserMayStartAsynchronously()) {
+ startup_task_runner_->StartRunningTasksAsync();
+ }
+#endif
+ }
+#if defined(OS_ANDROID)
+ if (!BrowserMayStartAsynchronously()) {
+ // A second request for asynchronous startup can be ignored, so
+ // StartupRunningTasksAsync is only called first time through. If, however,
+ // this is a request for synchronous startup then it must override any
+ // previous call for async startup, so we call RunAllTasksNow()
+ // unconditionally.
+ startup_task_runner_->RunAllTasksNow();
+ }
+#else
+ startup_task_runner_->RunAllTasksNow();
+#endif
}
int BrowserMainLoop::CreateThreads() {
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 83dbafd..b3ec73c 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -42,6 +42,7 @@ class BrowserThreadImpl;
class MediaStreamManager;
class ResourceDispatcherHostImpl;
class SpeechRecognitionManagerImpl;
+class StartupTaskRunner;
class SystemMessageWindowWin;
struct MainFunctionParams;
@@ -68,7 +69,10 @@ class CONTENT_EXPORT BrowserMainLoop {
void InitializeToolkit();
void MainMessageLoopStart();
- // Create the tasks we need to complete startup.
+ // Create and start running the tasks we need to complete startup. Note that
+ // this can be called more than once (currently only on Android) if we get a
+ // request for synchronous startup while the tasks created by asynchronous
+ // startup are still running.
void CreateStartupTasks();
// Perform the default message loop run logic.
@@ -141,6 +145,8 @@ class CONTENT_EXPORT BrowserMainLoop {
#elif defined(OS_MACOSX) && !defined(OS_IOS)
scoped_ptr<DeviceMonitorMac> device_monitor_mac_;
#endif
+ // The startup task runner is created by CreateStartupTasks()
+ scoped_ptr<StartupTaskRunner> startup_task_runner_;
// Destroy parts_ before main_message_loop_ (required) and before other
// classes constructed in content (but after main_thread_).
diff --git a/content/browser/browser_main_runner.cc b/content/browser/browser_main_runner.cc
index cca1466..f97318a 100644
--- a/content/browser/browser_main_runner.cc
+++ b/content/browser/browser_main_runner.cc
@@ -29,75 +29,82 @@ namespace content {
class BrowserMainRunnerImpl : public BrowserMainRunner {
public:
- BrowserMainRunnerImpl() : is_initialized_(false), is_shutdown_(false) {}
+ BrowserMainRunnerImpl()
+ : initialization_started_(false), is_shutdown_(false) {}
virtual ~BrowserMainRunnerImpl() {
- if (is_initialized_ && !is_shutdown_)
+ if (initialization_started_ && !is_shutdown_)
Shutdown();
}
- virtual int Initialize(const MainFunctionParams& parameters)
- OVERRIDE {
- TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize")
- is_initialized_ = true;
+ virtual int Initialize(const MainFunctionParams& parameters) OVERRIDE {
+ TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize");
+ // On Android we normally initialize the browser in a series of UI thread
+ // tasks. While this is happening a second request can come from the OS or
+ // another application to start the browser. If this happens then we must
+ // not run these parts of initialization twice.
+ if (!initialization_started_) {
+ initialization_started_ = true;
#if !defined(OS_IOS)
- if (parameters.command_line.HasSwitch(switches::kWaitForDebugger))
- base::debug::WaitForDebugger(60, true);
+ if (parameters.command_line.HasSwitch(switches::kWaitForDebugger))
+ base::debug::WaitForDebugger(60, true);
#endif
#if defined(OS_WIN)
- if (parameters.command_line.HasSwitch(
- switches::kEnableTextServicesFramework)) {
- base::win::SetForceToUseTSF();
- } else if (base::win::GetVersion() < base::win::VERSION_VISTA) {
- // When "Extend support of advanced text services to all programs"
- // (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on
- // Windows XP and handwriting modules shipped with Office 2003 are
- // installed, "penjpn.dll" and "skchui.dll" will be loaded and then crash
- // unless a user installs Office 2003 SP3. To prevent these modules from
- // being loaded, disable TSF entirely. crbug/160914.
- // TODO(yukawa): Add a high-level wrapper for this instead of calling
- // Win32 API here directly.
- ImmDisableTextFrameService(static_cast<DWORD>(-1));
- }
+ if (parameters.command_line.HasSwitch(
+ switches::kEnableTextServicesFramework)) {
+ base::win::SetForceToUseTSF();
+ } else if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ // When "Extend support of advanced text services to all programs"
+ // (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on
+ // Windows XP and handwriting modules shipped with Office 2003 are
+ // installed, "penjpn.dll" and "skchui.dll" will be loaded and then
+ // crash
+ // unless a user installs Office 2003 SP3. To prevent these modules from
+ // being loaded, disable TSF entirely. crbug/160914.
+ // TODO(yukawa): Add a high-level wrapper for this instead of calling
+ // Win32 API here directly.
+ ImmDisableTextFrameService(static_cast<DWORD>(-1));
+ }
#endif // OS_WIN
- base::StatisticsRecorder::Initialize();
+ base::StatisticsRecorder::Initialize();
- notification_service_.reset(new NotificationServiceImpl);
+ notification_service_.reset(new NotificationServiceImpl);
#if defined(OS_WIN)
- // Ole must be initialized before starting message pump, so that TSF
- // (Text Services Framework) module can interact with the message pump
- // on Windows 8 Metro mode.
- ole_initializer_.reset(new ui::ScopedOleInitializer);
+ // Ole must be initialized before starting message pump, so that TSF
+ // (Text Services Framework) module can interact with the message pump
+ // on Windows 8 Metro mode.
+ ole_initializer_.reset(new ui::ScopedOleInitializer);
#endif // OS_WIN
- main_loop_.reset(new BrowserMainLoop(parameters));
+ main_loop_.reset(new BrowserMainLoop(parameters));
- main_loop_->Init();
+ main_loop_->Init();
- main_loop_->EarlyInitialization();
+ main_loop_->EarlyInitialization();
- // Must happen before we try to use a message loop or display any UI.
- main_loop_->InitializeToolkit();
+ // Must happen before we try to use a message loop or display any UI.
+ main_loop_->InitializeToolkit();
- main_loop_->MainMessageLoopStart();
+ main_loop_->MainMessageLoopStart();
- // WARNING: If we get a WM_ENDSESSION, objects created on the stack here
- // are NOT deleted. If you need something to run during WM_ENDSESSION add it
- // to browser_shutdown::Shutdown or BrowserProcess::EndSession.
+// WARNING: If we get a WM_ENDSESSION, objects created on the stack here
+// are NOT deleted. If you need something to run during WM_ENDSESSION add it
+// to browser_shutdown::Shutdown or BrowserProcess::EndSession.
#if defined(OS_WIN) && !defined(NO_TCMALLOC)
- // When linking shared libraries, NO_TCMALLOC is defined, and dynamic
- // allocator selection is not supported.
+ // When linking shared libraries, NO_TCMALLOC is defined, and dynamic
+ // allocator selection is not supported.
- // Make this call before going multithreaded, or spawning any subprocesses.
- base::allocator::SetupSubprocessAllocator();
+ // Make this call before going multithreaded, or spawning any
+ // subprocesses.
+ base::allocator::SetupSubprocessAllocator();
#endif
- ui::InitializeInputMethod();
-
+ ui::InitializeInputMethod();
+ }
main_loop_->CreateStartupTasks();
int result_code = main_loop_->GetResultCode();
if (result_code > 0)
@@ -108,14 +115,14 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
}
virtual int Run() OVERRIDE {
- DCHECK(is_initialized_);
+ DCHECK(initialization_started_);
DCHECK(!is_shutdown_);
main_loop_->RunMainMessageLoopParts();
return main_loop_->GetResultCode();
}
virtual void Shutdown() OVERRIDE {
- DCHECK(is_initialized_);
+ DCHECK(initialization_started_);
DCHECK(!is_shutdown_);
g_exited_main_message_loop = true;
@@ -134,8 +141,8 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
}
protected:
- // True if the runner has been initialized.
- bool is_initialized_;
+ // True if we have started to initialize the runner.
+ bool initialization_started_;
// True if the runner has been shut down.
bool is_shutdown_;
diff --git a/content/browser/startup_task_runner.cc b/content/browser/startup_task_runner.cc
index a7e730e..d1fc904 100644
--- a/content/browser/startup_task_runner.cc
+++ b/content/browser/startup_task_runner.cc
@@ -11,40 +11,50 @@
namespace content {
StartupTaskRunner::StartupTaskRunner(
- bool browser_may_start_asynchronously,
base::Callback<void(int)> const startup_complete_callback,
scoped_refptr<base::SingleThreadTaskRunner> proxy)
- : asynchronous_startup_(browser_may_start_asynchronously),
- startup_complete_callback_(startup_complete_callback),
- proxy_(proxy) {}
+ : startup_complete_callback_(startup_complete_callback), proxy_(proxy) {}
+
+StartupTaskRunner::~StartupTaskRunner() {}
void StartupTaskRunner::AddTask(StartupTask& callback) {
task_list_.push_back(callback);
}
-void StartupTaskRunner::StartRunningTasks() {
+void StartupTaskRunner::StartRunningTasksAsync() {
DCHECK(proxy_);
int result = 0;
- if (asynchronous_startup_ && !task_list_.empty()) {
- const base::Closure next_task =
- base::Bind(&StartupTaskRunner::WrappedTask, this);
- proxy_->PostNonNestableTask(FROM_HERE, next_task);
- } else {
- for (std::list<StartupTask>::iterator it = task_list_.begin();
- it != task_list_.end();
- it++) {
- result = it->Run();
- if (result > 0) {
- break;
- }
- }
+ if (task_list_.empty()) {
if (!startup_complete_callback_.is_null()) {
startup_complete_callback_.Run(result);
}
+ } else {
+ const base::Closure next_task =
+ base::Bind(&StartupTaskRunner::WrappedTask, base::Unretained(this));
+ proxy_->PostNonNestableTask(FROM_HERE, next_task);
+ }
+}
+
+void StartupTaskRunner::RunAllTasksNow() {
+ int result = 0;
+ for (std::list<StartupTask>::iterator it = task_list_.begin();
+ it != task_list_.end();
+ it++) {
+ result = it->Run();
+ if (result > 0) break;
+ }
+ if (!startup_complete_callback_.is_null()) {
+ startup_complete_callback_.Run(result);
}
}
void StartupTaskRunner::WrappedTask() {
+ if (task_list_.empty()) {
+ // This will happen if the remaining tasks have been run synchronously since
+ // the WrappedTask was created. Any callback will already have been called,
+ // so there is nothing to do
+ return;
+ }
int result = task_list_.front().Run();
task_list_.pop_front();
if (result > 0 || task_list_.empty()) {
@@ -53,11 +63,9 @@ void StartupTaskRunner::WrappedTask() {
}
} else {
const base::Closure next_task =
- base::Bind(&StartupTaskRunner::WrappedTask, this);
+ base::Bind(&StartupTaskRunner::WrappedTask, base::Unretained(this));
proxy_->PostNonNestableTask(FROM_HERE, next_task);
}
}
-StartupTaskRunner::~StartupTaskRunner() {}
-
} // namespace content
diff --git a/content/browser/startup_task_runner.h b/content/browser/startup_task_runner.h
index 5f954ed..80e5627 100644
--- a/content/browser/startup_task_runner.h
+++ b/content/browser/startup_task_runner.h
@@ -8,7 +8,6 @@
#include <list>
#include "base/callback.h"
-#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "build/build_config.h"
@@ -32,30 +31,31 @@ typedef base::Callback<int(void)> StartupTask;
// no opportunity to handle UI events between the tasks of a
// SingleThreadedTaskRunner.
-class CONTENT_EXPORT StartupTaskRunner
- : public base::RefCounted<StartupTaskRunner> {
+class CONTENT_EXPORT StartupTaskRunner {
public:
// Constructor: Note that |startup_complete_callback| is optional. If it is
// not null it will be called once all the startup tasks have run.
- StartupTaskRunner(bool browser_may_start_asynchronously,
- base::Callback<void(int)> startup_complete_callback,
+ StartupTaskRunner(base::Callback<void(int)> startup_complete_callback,
scoped_refptr<base::SingleThreadTaskRunner> proxy);
+ ~StartupTaskRunner();
+
// Add a task to the queue of startup tasks to be run.
- virtual void AddTask(StartupTask& callback);
+ void AddTask(StartupTask& callback);
+
+ // Start running the tasks asynchronously.
+ void StartRunningTasksAsync();
- // Start running the tasks.
- virtual void StartRunningTasks();
+ // Run all tasks, or all remaining tasks, synchronously
+ void RunAllTasksNow();
private:
friend class base::RefCounted<StartupTaskRunner>;
- virtual ~StartupTaskRunner();
std::list<StartupTask> task_list_;
void WrappedTask();
- const bool asynchronous_startup_;
base::Callback<void(int)> startup_complete_callback_;
scoped_refptr<base::SingleThreadTaskRunner> proxy_;
diff --git a/content/browser/startup_task_runner_unittest.cc b/content/browser/startup_task_runner_unittest.cc
index 2efa79f..b63f555 100644
--- a/content/browser/startup_task_runner_unittest.cc
+++ b/content/browser/startup_task_runner_unittest.cc
@@ -116,20 +116,19 @@ TEST_F(StartupTaskRunnerTest, SynchronousExecution) {
EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
- scoped_refptr<StartupTaskRunner> runner =
- new StartupTaskRunner(false, base::Bind(&Observer), proxy);
+ StartupTaskRunner runner(base::Bind(&Observer), proxy);
StartupTask task1 =
base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
- runner->AddTask(task1);
+ runner.AddTask(task1);
EXPECT_EQ(GetLastTask(), 0);
StartupTask task2 =
base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
- runner->AddTask(task2);
+ runner.AddTask(task2);
// Nothing should run until we tell them to.
EXPECT_EQ(GetLastTask(), 0);
- runner->StartRunningTasks();
+ runner.RunAllTasksNow();
// On an immediate StartupTaskRunner the tasks should now all have run.
EXPECT_EQ(GetLastTask(), 2);
@@ -145,20 +144,19 @@ TEST_F(StartupTaskRunnerTest, NullObserver) {
EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
- scoped_refptr<StartupTaskRunner> runner =
- new StartupTaskRunner(false, base::Callback<void(int)>(), proxy);
+ StartupTaskRunner runner(base::Callback<void(int)>(), proxy);
StartupTask task1 =
base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
- runner->AddTask(task1);
+ runner.AddTask(task1);
EXPECT_EQ(GetLastTask(), 0);
StartupTask task2 =
base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
- runner->AddTask(task2);
+ runner.AddTask(task2);
// Nothing should run until we tell them to.
EXPECT_EQ(GetLastTask(), 0);
- runner->StartRunningTasks();
+ runner.RunAllTasksNow();
// On an immediate StartupTaskRunner the tasks should now all have run.
EXPECT_EQ(GetLastTask(), 2);
@@ -173,20 +171,19 @@ TEST_F(StartupTaskRunnerTest, SynchronousExecutionFailedTask) {
EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
- scoped_refptr<StartupTaskRunner> runner =
- new StartupTaskRunner(false, base::Bind(&Observer), proxy);
+ StartupTaskRunner runner(base::Bind(&Observer), proxy);
StartupTask task3 =
base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this));
- runner->AddTask(task3);
+ runner.AddTask(task3);
EXPECT_EQ(GetLastTask(), 0);
StartupTask task2 =
base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
- runner->AddTask(task2);
+ runner.AddTask(task2);
// Nothing should run until we tell them to.
EXPECT_EQ(GetLastTask(), 0);
- runner->StartRunningTasks();
+ runner.RunAllTasksNow();
// Only the first task should have run, since it failed
EXPECT_EQ(GetLastTask(), 3);
@@ -207,19 +204,18 @@ TEST_F(StartupTaskRunnerTest, AsynchronousExecution) {
.Times(testing::Between(2, 3))
.WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg)));
- scoped_refptr<StartupTaskRunner> runner =
- new StartupTaskRunner(true, base::Bind(&Observer), proxy);
+ StartupTaskRunner runner(base::Bind(&Observer), proxy);
StartupTask task1 =
base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
- runner->AddTask(task1);
+ runner.AddTask(task1);
StartupTask task2 =
base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
- runner->AddTask(task2);
+ runner.AddTask(task2);
// Nothing should run until we tell them to.
EXPECT_EQ(GetLastTask(), 0);
- runner->StartRunningTasks();
+ runner.StartRunningTasksAsync();
// No tasks should have run yet, since we the message loop hasn't run.
EXPECT_EQ(GetLastTask(), 0);
@@ -248,19 +244,18 @@ TEST_F(StartupTaskRunnerTest, AsynchronousExecutionFailedTask) {
.Times(testing::Between(1, 2))
.WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg)));
- scoped_refptr<StartupTaskRunner> runner =
- new StartupTaskRunner(true, base::Bind(&Observer), proxy);
+ StartupTaskRunner runner(base::Bind(&Observer), proxy);
StartupTask task3 =
base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this));
- runner->AddTask(task3);
+ runner.AddTask(task3);
StartupTask task2 =
base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
- runner->AddTask(task2);
+ runner.AddTask(task2);
// Nothing should run until we tell them to.
EXPECT_EQ(GetLastTask(), 0);
- runner->StartRunningTasks();
+ runner.StartRunningTasksAsync();
// No tasks should have run yet, since we the message loop hasn't run.
EXPECT_EQ(GetLastTask(), 0);
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 064a12e..28ff4c1 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -233,8 +233,6 @@
'browser/accessibility/browser_accessibility_state_impl.h',
'browser/accessibility/browser_accessibility_win.cc',
'browser/accessibility/browser_accessibility_win.h',
- 'browser/android/android_browser_process.cc',
- 'browser/android/android_browser_process.h',
'browser/android/browser_jni_registrar.cc',
'browser/android/browser_jni_registrar.h',
'browser/android/browser_media_player_manager.cc',
diff --git a/content/content_jni.gypi b/content/content_jni.gypi
index c10f738..cd83aa6 100644
--- a/content/content_jni.gypi
+++ b/content/content_jni.gypi
@@ -12,7 +12,6 @@
'public/android/java/src/org/chromium/content/app/ContentMain.java',
'public/android/java/src/org/chromium/content/app/LibraryLoader.java',
'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java',
- 'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java',
'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java',
'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java',
'public/android/java/src/org/chromium/content/browser/ContentSettings.java',
diff --git a/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java b/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java
index b1b72ba..00a55db 100644
--- a/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java
+++ b/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java
@@ -4,106 +4,24 @@
package org.chromium.content.browser;
-import android.app.ActivityManager;
import android.content.Context;
-import android.util.Log;
import org.chromium.base.JNINamespace;
-import org.chromium.content.app.ContentMain;
-import org.chromium.content.app.LibraryLoader;
import org.chromium.content.common.ProcessInitException;
-@JNINamespace("content")
-// TODO(nyquist) Remove this class, and move the functionality to BrowserStartupController.
+@JNINamespace("content") // TODO(nyquist) Remove this class, and move the functionality to
+ // BrowserStartupController.
public class AndroidBrowserProcess {
- private static final String TAG = "BrowserProcessMain";
- // Prevents initializing the process more than once.
- private static boolean sInitialized = false;
+ public static final int MAX_RENDERERS_LIMIT = BrowserStartupController.MAX_RENDERERS_LIMIT;
- // Use single-process mode that runs the renderer on a separate thread in the main application.
- public static final int MAX_RENDERERS_SINGLE_PROCESS = 0;
-
- // Cap on the maximum number of renderer processes that can be requested.
- // This is currently set to account for:
- // 13: The maximum number of sandboxed processes we have available
- // - 1: The regular New Tab Page
- // - 1: The incognito New Tab Page
- // - 1: A regular incognito tab
- // - 1: Safety buffer (http://crbug.com/251279)
- public static final int MAX_RENDERERS_LIMIT =
- ChildProcessLauncher.MAX_REGISTERED_SANDBOXED_SERVICES - 4;
-
- /**
- * Initialize the process as a ContentView host. This must be called from the main UI thread.
- * This should be called by the ContentView constructor to prepare this process for ContentView
- * use outside of the browser. In the case where ContentView is used in the browser then
- * initBrowserProcess() should already have been called and this is a no-op.
- *
- * @param context Context used to obtain the application context.
- * @param maxRendererProcesses Limit on the number of renderers to use. Each tab runs in its own
- * process until the maximum number of processes is reached. The special value of
- * MAX_RENDERERS_SINGLE_PROCESS requests single-process mode where the renderer will run in the
- * application process in a separate thread. The maximum number of allowed renderers is capped
- * by MAX_RENDERERS_LIMIT.
- *
- * @return Whether the process actually needed to be initialized (false if already running).
- */
- public static boolean init(Context context, int maxRendererProcesses)
- throws ProcessInitException {
- assert maxRendererProcesses >= 0;
- assert maxRendererProcesses <= MAX_RENDERERS_LIMIT;
- if (sInitialized) return false;
- sInitialized = true;
- Log.i(TAG, "Initializing chromium process, renderers=" + maxRendererProcesses);
-
- // Normally Main.java will have kicked this off asynchronously for Chrome. But other
- // ContentView apps like tests also need them so we make sure we've extracted resources
- // here. We can still make it a little async (wait until the library is loaded).
- ResourceExtractor resourceExtractor = ResourceExtractor.get(context);
- resourceExtractor.startExtractingResources();
-
- // Normally Main.java will have already loaded the library asynchronously, we only need to
- // load it here if we arrived via another flow, e.g. bookmark access & sync setup.
- LibraryLoader.ensureInitialized();
- // TODO(yfriedman): Remove dependency on a command line flag for this.
- DeviceUtils.addDeviceSpecificUserAgentSwitch(context);
-
- Context appContext = context.getApplicationContext();
- // Now we really need to have the resources ready.
- resourceExtractor.waitForCompletion();
-
- nativeSetCommandLineFlags(maxRendererProcesses,
- nativeIsPluginEnabled() ? getPlugins(context) : null);
- ContentMain.initApplicationContext(appContext);
- int result = ContentMain.start();
- if (result > 0) throw new ProcessInitException(result);
- return true;
- }
-
- /**
- * Initialization needed for tests. Mainly used by content browsertests.
- */
- public static void initChromiumBrowserProcessForTests(Context context) {
- ResourceExtractor resourceExtractor = ResourceExtractor.get(context);
- resourceExtractor.startExtractingResources();
- resourceExtractor.waitForCompletion();
-
- // Having a single renderer should be sufficient for tests. We can't have more than
- // MAX_RENDERERS_LIMIT.
- nativeSetCommandLineFlags(1 /* maxRenderers */, null);
- }
-
- private static String getPlugins(final Context context) {
- return PepperPluginManager.getPlugins(context);
+ // Temporarily provide the old init, to simplify landing patches
+ // TODO(aberent) Remove
+ @Deprecated
+ public static void init(Context context, int maxRendererProcesses) throws ProcessInitException {
+ if (!BrowserStartupController.get(context).startBrowserProcessesSync(
+ maxRendererProcesses)) {
+ throw new ProcessInitException(1);
+ }
}
-
- private static native void nativeSetCommandLineFlags(int maxRenderProcesses,
- String pluginDescriptor);
-
- // Is this an official build of Chrome? Only native code knows for sure. Official build
- // knowledge is needed very early in process startup.
- private static native boolean nativeIsOfficialBuild();
-
- private static native boolean nativeIsPluginEnabled();
}
diff --git a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
index e6b3a67..b72d60d 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
@@ -13,6 +13,8 @@ import com.google.common.annotations.VisibleForTesting;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.base.ThreadUtils;
+import org.chromium.content.app.ContentMain;
+import org.chromium.content.app.LibraryLoader;
import org.chromium.content.common.ProcessInitException;
import java.util.ArrayList;
@@ -55,12 +57,13 @@ public class BrowserStartupController {
private static boolean sBrowserMayStartAsynchronously = false;
- private static void setAsynchronousStartupConfig() {
- sBrowserMayStartAsynchronously = true;
+ private static void setAsynchronousStartup(boolean enable) {
+ sBrowserMayStartAsynchronously = enable;
}
+ @VisibleForTesting
@CalledByNative
- private static boolean browserMayStartAsynchonously() {
+ static boolean browserMayStartAsynchonously() {
return sBrowserMayStartAsynchronously;
}
@@ -79,13 +82,27 @@ public class BrowserStartupController {
// The context is set on creation, but the reference is cleared after the browser process
// initialization has been started, since it is not needed anymore. This is to ensure the
// context is not leaked.
- private Context mContext;
+ private final Context mContext;
// Whether the async startup of the browser process has started.
private boolean mHasStartedInitializingBrowserProcess;
// Whether the async startup of the browser process is complete.
- private boolean mAsyncStartupDone;
+ private boolean mStartupDone;
+
+ // Use single-process mode that runs the renderer on a separate thread in
+ // the main application.
+ public static final int MAX_RENDERERS_SINGLE_PROCESS = 0;
+
+ // Cap on the maximum number of renderer processes that can be requested.
+ // This is currently set to account for:
+ // 13: The maximum number of sandboxed processes we have available
+ // - 1: The regular New Tab Page
+ // - 1: The incognito New Tab Page
+ // - 1: A regular incognito tab
+ // - 1: Safety buffer (http://crbug.com/251279)
+ public static final int MAX_RENDERERS_LIMIT =
+ ChildProcessLauncher.MAX_REGISTERED_SANDBOXED_SERVICES - 4;
// This field is set after startup has been completed based on whether the startup was a success
// or not. It is used when later requests to startup come in that happen after the initial set
@@ -124,7 +141,7 @@ public class BrowserStartupController {
*/
public void startBrowserProcessesAsync(final StartupCallback callback) {
assert ThreadUtils.runningOnUiThread() : "Tried to start the browser on the wrong thread.";
- if (mAsyncStartupDone) {
+ if (mStartupDone) {
// Browser process initialization has already been completed, so we can immediately post
// the callback.
postStartupCompleted(callback);
@@ -139,62 +156,93 @@ public class BrowserStartupController {
// flag that indicates that we have kicked off starting the browser process.
mHasStartedInitializingBrowserProcess = true;
- enableAsynchronousStartup();
+ if (!tryPrepareToStartBrowserProcess(MAX_RENDERERS_LIMIT)) return;
- // Try to initialize the Android browser process.
- tryToInitializeBrowserProcess();
+ setAsynchronousStartup(true);
+ if (contentStart() > 0) {
+ // Failed. The callbacks may not have run, so run them.
+ enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
+ }
}
}
- private void tryToInitializeBrowserProcess() {
+ private boolean tryPrepareToStartBrowserProcess(int maxRenderers) {
+ // Make sure that everything is in place to initialize the Android browser process.
try {
- assert mContext != null;
- boolean wasAlreadyInitialized = initializeAndroidBrowserProcess();
- // The context is not needed anymore, so clear the member field to not leak.
- mContext = null;
- if (wasAlreadyInitialized) {
- // Something has already initialized the browser process before we got to setup the
- // async startup. This means that we will never get a callback, so manually call
- // them now, and just assume that the startup was successful.
- Log.w(TAG, "Browser process was initialized without BrowserStartupController");
- enqueueCallbackExecution(STARTUP_SUCCESS, ALREADY_STARTED);
- }
+ prepareToStartBrowserProcess(maxRenderers);
+ return true;
} catch (ProcessInitException e) {
- Log.e(TAG, "Unable to start browser process.", e);
- // ProcessInitException could mean one of two things:
- // 1) The LibraryLoader failed.
- // 2) ContentMain failed to start.
- // It is unclear whether the browser tasks have already been started, and in case they
- // have not, post a message to execute all the callbacks. Whichever call to
- // executeEnqueuedCallbacks comes first will trigger the callbacks, but since the list
- // of callbacks is then cleared, they will only be called once.
+ Log.e(TAG, "Unable to load native library.", e);
enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
+ return false;
}
}
+ /**
+ * Start the browser process synchronously. If the browser is already being started
+ * asynchronously then complete startup synchronously
+ *
+ * <p/>
+ * Note that this can only be called on the UI thread.
+ *
+ * @param max_renderers The maximum number of renderer processes the browser may
+ * create. Zero for single process mode.
+ * @return true if successfully started, false otherwise.
+ */
+ public boolean startBrowserProcessesSync(int maxRenderers) {
+ if (mStartupDone) {
+ // Nothing to do
+ return mStartupSuccess;
+ }
+ if (!mHasStartedInitializingBrowserProcess) {
+ if (!tryPrepareToStartBrowserProcess(maxRenderers)) return false;
+ }
+
+ setAsynchronousStartup(false);
+ if (contentStart() > 0) {
+ // Failed. The callbacks may not have run, so run them.
+ enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
+ }
+
+ // Startup should now be complete
+ assert mStartupDone;
+ return mStartupSuccess;
+ }
+
+ /**
+ * Wrap ContentMain.start() for testing.
+ */
+ @VisibleForTesting
+ int contentStart() {
+ return ContentMain.start();
+ }
+
public void addStartupCompletedObserver(StartupCallback callback) {
ThreadUtils.assertOnUiThread();
- if (mAsyncStartupDone)
+ if (mStartupDone) {
postStartupCompleted(callback);
- else
+ } else {
mAsyncStartupCallbacks.add(callback);
+ }
}
private void executeEnqueuedCallbacks(int startupResult, boolean alreadyStarted) {
assert ThreadUtils.runningOnUiThread() : "Callback from browser startup from wrong thread.";
- mAsyncStartupDone = true;
+ mStartupDone = true;
+ mStartupSuccess = (startupResult <= 0);
for (StartupCallback asyncStartupCallback : mAsyncStartupCallbacks) {
- if (startupResult > 0) {
- asyncStartupCallback.onFailure();
- } else {
- mStartupSuccess = true;
+ if (mStartupSuccess) {
asyncStartupCallback.onSuccess(alreadyStarted);
+ } else {
+ asyncStartupCallback.onFailure();
}
}
// We don't want to hold on to any objects after we do not need them anymore.
mAsyncStartupCallbacks.clear();
}
+ // Queue the callbacks to run. Since running the callbacks clears the list it is safe to call
+ // this more than once.
private void enqueueCallbackExecution(final int startupFailure, final boolean alreadyStarted) {
new Handler().post(new Runnable() {
@Override
@@ -208,28 +256,64 @@ public class BrowserStartupController {
new Handler().post(new Runnable() {
@Override
public void run() {
- if (mStartupSuccess)
+ if (mStartupSuccess) {
callback.onSuccess(ALREADY_STARTED);
- else
+ } else {
callback.onFailure();
+ }
}
});
}
- /**
- * Ensure that the browser process will be asynchronously started up. This also ensures that we
- * get a call to {@link #browserStartupComplete} when the browser startup is complete.
- */
@VisibleForTesting
- void enableAsynchronousStartup() {
- setAsynchronousStartupConfig();
+ void prepareToStartBrowserProcess(int maxRendererProcesses) throws ProcessInitException {
+ Log.i(TAG, "Initializing chromium process, renderers=" + maxRendererProcesses);
+
+ // Normally Main.java will have kicked this off asynchronously for Chrome. But other
+ // ContentView apps like tests also need them so we make sure we've extracted resources
+ // here. We can still make it a little async (wait until the library is loaded).
+ ResourceExtractor resourceExtractor = ResourceExtractor.get(mContext);
+ resourceExtractor.startExtractingResources();
+
+ // Normally Main.java will have already loaded the library asynchronously, we only need
+ // to load it here if we arrived via another flow, e.g. bookmark access & sync setup.
+ LibraryLoader.ensureInitialized();
+
+ // TODO(yfriedman): Remove dependency on a command line flag for this.
+ DeviceUtils.addDeviceSpecificUserAgentSwitch(mContext);
+
+ Context appContext = mContext.getApplicationContext();
+ // Now we really need to have the resources ready.
+ resourceExtractor.waitForCompletion();
+
+ nativeSetCommandLineFlags(maxRendererProcesses,
+ nativeIsPluginEnabled() ? getPlugins() : null);
+ ContentMain.initApplicationContext(appContext);
}
/**
- * @return whether the process was already initialized, so native was not instructed to start.
+ * Initialization needed for tests. Mainly used by content browsertests.
*/
- @VisibleForTesting
- boolean initializeAndroidBrowserProcess() throws ProcessInitException {
- return !AndroidBrowserProcess.init(mContext, AndroidBrowserProcess.MAX_RENDERERS_LIMIT);
+ public void initChromiumBrowserProcessForTests() {
+ ResourceExtractor resourceExtractor = ResourceExtractor.get(mContext);
+ resourceExtractor.startExtractingResources();
+ resourceExtractor.waitForCompletion();
+
+ // Having a single renderer should be sufficient for tests. We can't have more than
+ // MAX_RENDERERS_LIMIT.
+ nativeSetCommandLineFlags(1 /* maxRenderers */, null);
}
+
+ private String getPlugins() {
+ return PepperPluginManager.getPlugins(mContext);
+ }
+
+ private static native void nativeSetCommandLineFlags(int maxRenderProcesses,
+ String pluginDescriptor);
+
+ // Is this an official build of Chrome? Only native code knows for sure. Official build
+ // knowledge is needed very early in process startup.
+ private static native boolean nativeIsOfficialBuild();
+
+ private static native boolean nativeIsPluginEnabled();
}
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
index 541c96e..d60b04d 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
@@ -11,6 +11,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.AdvancedMockContext;
import org.chromium.content.common.ProcessInitException;
+import org.chromium.content.common.ResultCodes;
public class BrowserStartupControllerTest extends InstrumentationTestCase {
@@ -18,41 +19,40 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
private static class TestBrowserStartupController extends BrowserStartupController {
- private boolean mThrowProcessInitException;
private int mStartupResult;
- private boolean mAlreadyInitialized = false;
+ private boolean mLibraryLoadSucceeds;
private int mInitializedCounter = 0;
- private boolean mCallbackWasSetup;
- private TestBrowserStartupController(Context context) {
- super(context);
+ @Override
+ void prepareToStartBrowserProcess(int numRenderers) throws ProcessInitException {
+ if (!mLibraryLoadSucceeds) {
+ throw new ProcessInitException(ResultCodes.RESULT_CODE_NATIVE_LIBRARY_LOAD_FAILED);
+ }
}
- @Override
- void enableAsynchronousStartup() {
- mCallbackWasSetup = true;
+ private TestBrowserStartupController(Context context) {
+ super(context);
}
@Override
- boolean initializeAndroidBrowserProcess() throws ProcessInitException {
+ int contentStart() {
mInitializedCounter++;
- if (mThrowProcessInitException) {
- throw new ProcessInitException(4);
- }
- if (!mAlreadyInitialized) {
+ if(BrowserStartupController.browserMayStartAsynchonously()) {
// Post to the UI thread to emulate what would happen in a real scenario.
- ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ ThreadUtils.postOnUiThread(new Runnable() {
@Override
public void run() {
BrowserStartupController.browserStartupComplete(mStartupResult);
}
});
+ } else {
+ BrowserStartupController.browserStartupComplete(mStartupResult);
}
- return mAlreadyInitialized;
+ return mStartupResult;
}
- private boolean hasBeenInitializedOneTime() {
- return mInitializedCounter == 1;
+ private int initializedCounter() {
+ return mInitializedCounter;
}
}
@@ -92,6 +92,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
@SmallTest
public void testSingleAsynchronousStartupRequest() {
mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+ mController.mLibraryLoadSucceeds = true;
final TestStartupCallback callback = new TestStartupCallback();
// Kick off the asynchronous startup request.
@@ -102,9 +103,10 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
}
});
- assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
- assertTrue("The browser process should have been initialized one time.",
- mController.hasBeenInitializedOneTime());
+ assertTrue("Asynchronous mode should have been set.",
+ BrowserStartupController.browserMayStartAsynchonously());
+ assertEquals("The browser process should have been initialized one time.",
+ 1, mController.initializedCounter());
// Wait for callbacks to complete.
getInstrumentation().waitForIdleSync();
@@ -118,6 +120,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
@SmallTest
public void testMultipleAsynchronousStartupRequests() {
mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+ mController.mLibraryLoadSucceeds = true;
final TestStartupCallback callback1 = new TestStartupCallback();
final TestStartupCallback callback2 = new TestStartupCallback();
final TestStartupCallback callback3 = new TestStartupCallback();
@@ -142,9 +145,10 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
}
});
- assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
- assertTrue("The browser process should have been initialized one time.",
- mController.hasBeenInitializedOneTime());
+ assertTrue("Asynchronous mode should have been set.",
+ BrowserStartupController.browserMayStartAsynchonously());
+ assertEquals("The browser process should have been initialized one time.",
+ 1, mController.initializedCounter());
// Wait for callbacks to complete.
getInstrumentation().waitForIdleSync();
@@ -164,6 +168,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
@SmallTest
public void testConsecutiveAsynchronousStartupRequests() {
mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+ mController.mLibraryLoadSucceeds = true;
final TestStartupCallback callback1 = new TestStartupCallback();
final TestStartupCallback callback2 = new TestStartupCallback();
@@ -181,9 +186,10 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
}
});
- assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
- assertTrue("The browser process should have been initialized one time.",
- mController.hasBeenInitializedOneTime());
+ assertTrue("Asynchronous mode should have been set.",
+ BrowserStartupController.browserMayStartAsynchonously());
+ assertEquals("The browser process should have been initialized one time.",
+ 1, mController.initializedCounter());
// Wait for callbacks to complete.
getInstrumentation().waitForIdleSync();
@@ -226,6 +232,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
@SmallTest
public void testSingleFailedAsynchronousStartupRequest() {
mController.mStartupResult = BrowserStartupController.STARTUP_FAILURE;
+ mController.mLibraryLoadSucceeds = true;
final TestStartupCallback callback = new TestStartupCallback();
// Kick off the asynchronous startup request.
@@ -236,9 +243,10 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
}
});
- assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
- assertTrue("The browser process should have been initialized one time.",
- mController.hasBeenInitializedOneTime());
+ assertTrue("Asynchronous mode should have been set.",
+ BrowserStartupController.browserMayStartAsynchonously());
+ assertEquals("The browser process should have been initialized one time.",
+ 1, mController.initializedCounter());
// Wait for callbacks to complete.
getInstrumentation().waitForIdleSync();
@@ -250,6 +258,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
@SmallTest
public void testConsecutiveFailedAsynchronousStartupRequests() {
mController.mStartupResult = BrowserStartupController.STARTUP_FAILURE;
+ mController.mLibraryLoadSucceeds = true;
final TestStartupCallback callback1 = new TestStartupCallback();
final TestStartupCallback callback2 = new TestStartupCallback();
@@ -267,9 +276,10 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
}
});
- assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
- assertTrue("The browser process should have been initialized one time.",
- mController.hasBeenInitializedOneTime());
+ assertTrue("Asynchronous mode should have been set.",
+ BrowserStartupController.browserMayStartAsynchonously());
+ assertEquals("The browser process should have been initialized one time.",
+ 1, mController.initializedCounter());
// Wait for callbacks to complete.
getInstrumentation().waitForIdleSync();
@@ -306,11 +316,76 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
}
@SmallTest
- public void testAndroidBrowserStartupThrowsException() {
- mController.mThrowProcessInitException = true;
+ public void testSingleSynchronousRequest() {
+ mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+ mController.mLibraryLoadSucceeds = true;
+ // Kick off the synchronous startup.
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue("Browser should have started successfully",
+ mController.startBrowserProcessesSync(1));
+ }
+ });
+ assertFalse("Synchronous mode should have been set",
+ BrowserStartupController.browserMayStartAsynchonously());
+
+ assertEquals("The browser process should have been initialized one time.",
+ 1, mController.initializedCounter());
+ }
+
+ @SmallTest
+ public void testAsyncThenSyncRequests() {
+ mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+ mController.mLibraryLoadSucceeds = true;
final TestStartupCallback callback = new TestStartupCallback();
- // Kick off the asynchronous startup request.
+ // Kick off the startups.
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ mController.startBrowserProcessesAsync(callback);
+ // To ensure that the async startup doesn't complete too soon we have
+ // to do both these in a since Runnable instance. This avoids the
+ // unpredictable race that happens in real situations.
+ assertTrue("Browser should have started successfully",
+ mController.startBrowserProcessesSync(1));
+ }
+ });
+ assertFalse("Synchronous mode should have been set",
+ BrowserStartupController.browserMayStartAsynchonously());
+
+ assertEquals("The browser process should have been initialized twice.",
+ 2, mController.initializedCounter());
+
+ assertTrue("Callback should have been executed.", callback.mHasStartupResult);
+ assertTrue("Callback should have been a success.", callback.mWasSuccess);
+ assertFalse("Callback should be told that the browser process was not already started.",
+ callback.mAlreadyStarted);
+ }
+
+ @SmallTest
+ public void testSyncThenAsyncRequests() {
+ mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+ mController.mLibraryLoadSucceeds = true;
+ final TestStartupCallback callback = new TestStartupCallback();
+
+ // Do a synchronous startup first.
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue("Browser should have started successfully",
+ mController.startBrowserProcessesSync(1));
+ }
+ });
+
+ assertEquals("The browser process should have been initialized once.",
+ 1, mController.initializedCounter());
+
+ assertFalse("Synchronous mode should have been set",
+ BrowserStartupController.browserMayStartAsynchonously());
+
+ // Kick off the asynchronous startup request. This should just queue the callback.
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
@@ -318,20 +393,21 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
}
});
- assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
- assertTrue("The browser process should have been initialized one time.",
- mController.hasBeenInitializedOneTime());
+ assertEquals("The browser process should not have been initialized a second time.",
+ 1, mController.initializedCounter());
// Wait for callbacks to complete.
getInstrumentation().waitForIdleSync();
assertTrue("Callback should have been executed.", callback.mHasStartupResult);
- assertTrue("Callback should have been a failure.", callback.mWasFailure);
+ assertTrue("Callback should have been a success.", callback.mWasSuccess);
+ assertTrue("Callback should be told that the browser process was already started.",
+ callback.mAlreadyStarted);
}
@SmallTest
- public void testAndroidBrowserProcessAlreadyInitializedByOtherPartsOfCode() {
- mController.mAlreadyInitialized = true;
+ public void testLibraryLoadFails() {
+ mController.mLibraryLoadSucceeds = false;
final TestStartupCallback callback = new TestStartupCallback();
// Kick off the asynchronous startup request.
@@ -342,16 +418,16 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
}
});
- assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
- assertTrue("The browser process should have been initialized one time.",
- mController.hasBeenInitializedOneTime());
+ assertEquals("The browser process should not have been initialized.",
+ 0, mController.initializedCounter());
// Wait for callbacks to complete.
getInstrumentation().waitForIdleSync();
assertTrue("Callback should have been executed.", callback.mHasStartupResult);
- assertTrue("Callback should have been a success.", callback.mWasSuccess);
- assertTrue("Callback should be told that the browser process was already started.",
+ assertFalse("Callback should have been a failure.", callback.mWasSuccess);
+ assertFalse("Callback should be told that the browser process was not already started.",
callback.mAlreadyStarted);
}
+
}
diff --git a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
index 3f25e68..d7aeecf 100644
--- a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
+++ b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
@@ -7,13 +7,13 @@ package org.chromium.content_browsertests_apk;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.util.Log;
import org.chromium.base.JNINamespace;
import org.chromium.content.app.LibraryLoader;
-import org.chromium.content.browser.AndroidBrowserProcess;
+import org.chromium.content.browser.BrowserStartupController;
import org.chromium.content.common.ProcessInitException;
import org.chromium.content_shell.ShellManager;
import org.chromium.ui.WindowAndroid;
@@ -34,7 +34,7 @@ public class ContentBrowserTestsActivity extends Activity {
} catch (ProcessInitException e) {
Log.i(TAG, "Cannot load content_browsertests:" + e);
}
- AndroidBrowserProcess.initChromiumBrowserProcessForTests(getApplicationContext());
+ BrowserStartupController.get(getApplicationContext()).initChromiumBrowserProcessForTests();
LayoutInflater inflater =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
index 2d819e9..c98ba12 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
@@ -18,7 +18,6 @@ import org.chromium.base.ChromiumActivity;
import org.chromium.base.MemoryPressureListener;
import org.chromium.content.app.LibraryLoader;
import org.chromium.content.browser.ActivityContentVideoViewClient;
-import org.chromium.content.browser.AndroidBrowserProcess;
import org.chromium.content.browser.BrowserStartupController;
import org.chromium.content.browser.ContentVideoViewClient;
import org.chromium.content.browser.ContentView;
@@ -47,15 +46,15 @@ public class ContentShellActivity extends ChromiumActivity {
public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs";
/**
- * Sending an intent with this action will simulate a memory pressure signal
- * at a critical level.
+ * Sending an intent with this action will simulate a memory pressure signal at a critical
+ * level.
*/
private static final String ACTION_LOW_MEMORY =
"org.chromium.content_shell.action.ACTION_LOW_MEMORY";
/**
- * Sending an intent with this action will simulate a memory pressure signal
- * at a moderate level.
+ * Sending an intent with this action will simulate a memory pressure signal at a moderate
+ * level.
*/
private static final String ACTION_TRIM_MEMORY_MODERATE =
"org.chromium.content_shell.action.ACTION_TRIM_MEMORY_MODERATE";
@@ -100,11 +99,10 @@ public class ContentShellActivity extends ChromiumActivity {
}
if (CommandLine.getInstance().hasSwitch(CommandLine.DUMP_RENDER_TREE)) {
- try {
- if (!AndroidBrowserProcess.init(this, AndroidBrowserProcess.MAX_RENDERERS_LIMIT)) {
- finishInitialization(savedInstanceState);
- }
- } catch (ProcessInitException e) {
+ if(BrowserStartupController.get(this).startBrowserProcessesSync(
+ BrowserStartupController.MAX_RENDERERS_LIMIT)) {
+ finishInitialization(savedInstanceState);
+ } else {
initializationFailed();
}
} else {