summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java27
-rw-r--r--chrome/browser/chrome_browser_main.h7
-rw-r--r--content/browser/android/browser_jni_registrar.cc63
-rw-r--r--content/browser/android/browser_startup_config.cc25
-rw-r--r--content/browser/android/browser_startup_config.h18
-rw-r--r--content/browser/browser_main_loop.cc73
-rw-r--r--content/browser/browser_main_loop.h18
-rw-r--r--content/browser/browser_main_runner.cc15
-rw-r--r--content/browser/startup_task_runner.cc63
-rw-r--r--content/browser/startup_task_runner.h66
-rw-r--r--content/browser/startup_task_runner_unittest.cc281
-rw-r--r--content/content_browser.gypi4
-rw-r--r--content/content_jni.gypi1
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/BrowserStartupConfig.java43
-rw-r--r--content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java35
-rw-r--r--content/shell/shell_browser_main_parts.h4
17 files changed, 667 insertions, 77 deletions
diff --git a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
index 98df0c1..b53c80d 100644
--- a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
+++ b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
@@ -16,7 +16,7 @@ import org.chromium.base.MemoryPressureListener;
import org.chromium.chrome.browser.DevToolsServer;
import org.chromium.content.browser.ActivityContentVideoViewClient;
import org.chromium.content.browser.AndroidBrowserProcess;
-import org.chromium.content.browser.ContentVideoView;
+import org.chromium.content.browser.BrowserStartupConfig;
import org.chromium.content.browser.ContentVideoViewClient;
import org.chromium.content.browser.ContentView;
import org.chromium.content.browser.ContentViewClient;
@@ -51,19 +51,39 @@ public class ChromiumTestShellActivity extends ChromiumActivity {
private DevToolsServer mDevToolsServer;
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!CommandLine.isInitialized()) CommandLine.initFromFile(COMMAND_LINE_FILE);
waitForDebuggerIfNeeded();
DeviceUtils.addDeviceSpecificUserAgentSwitch(this);
+
+ BrowserStartupConfig.setAsync(new BrowserStartupConfig.StartupCallback() {
+ @Override
+ public void run(int startupResult) {
+ if (startupResult > 0) {
+ // TODO: Show error message.
+ Log.e(TAG, "Chromium browser process initialization failed");
+ finish();
+ } else {
+ finishInitialization(savedInstanceState);
+ }
+ }
+ });
+
try {
- AndroidBrowserProcess.init(this, AndroidBrowserProcess.MAX_RENDERERS_LIMIT);
+ if (!AndroidBrowserProcess.init(this, AndroidBrowserProcess.MAX_RENDERERS_LIMIT)) {
+ // Process was already running, finish initialization now.
+ finishInitialization(savedInstanceState);
+ }
} catch (ProcessInitException e) {
Log.e(TAG, "Chromium browser process initialization failed", e);
finish();
}
+ }
+
+ private void finishInitialization(final Bundle savedInstanceState) {
setContentView(R.layout.testshell_activity);
mTabManager = (TabManager) findViewById(R.id.tab_manager);
String startupUrl = getUrlFromIntent(getIntent());
@@ -161,6 +181,7 @@ public class ChromiumTestShellActivity extends ChromiumActivity {
/**
* Creates a {@link TestShellTab} with a URL specified by {@code url}.
+ *
* @param url The URL the new {@link TestShellTab} should start with.
*/
public void createTab(String url) {
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index e7cac1d..10c1d8e 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -17,6 +17,7 @@
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "content/public/browser/browser_main_parts.h"
#include "content/public/browser/render_view_host.h"
+#include "content/public/common/main_function_params.h"
class ActiveTabTracker;
class BrowserProcessImpl;
@@ -41,10 +42,6 @@ namespace chrome_browser_metrics {
class TrackingSynchronizer;
}
-namespace content {
-struct MainFunctionParams;
-}
-
namespace performance_monitor {
class StartupTimer;
}
@@ -134,7 +131,7 @@ class ChromeBrowserMainParts : public content::BrowserMainParts {
// Members initialized on construction ---------------------------------------
- const content::MainFunctionParams& parameters_;
+ const content::MainFunctionParams parameters_;
const CommandLine& parsed_command_line_;
int result_code_;
diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc
index 25e0c3b..35da3af 100644
--- a/content/browser/android/browser_jni_registrar.cc
+++ b/content/browser/android/browser_jni_registrar.cc
@@ -9,6 +9,7 @@
#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_config.h"
#include "content/browser/android/child_process_launcher_android.h"
#include "content/browser/android/content_settings.h"
#include "content/browser/android/content_video_view.h"
@@ -36,37 +37,37 @@ using content::SurfaceTexturePeerBrowserImpl;
namespace {
base::android::RegistrationMethod kContentRegisteredMethods[] = {
- { "AndroidLocationApiAdapter",
- content::AndroidLocationApiAdapter::RegisterGeolocationService },
- { "AndroidBrowserProcess", content::RegisterAndroidBrowserProcess },
- { "BrowserAccessibilityManager",
- content::RegisterBrowserAccessibilityManager },
- { "ChildProcessLauncher", content::RegisterChildProcessLauncher },
- { "ContentSettings", content::ContentSettings::RegisterContentSettings },
- { "ContentViewRenderView",
- content::ContentViewRenderView::RegisterContentViewRenderView },
- { "ContentVideoView", content::ContentVideoView::RegisterContentVideoView },
- { "ContentViewCore", content::RegisterContentViewCore },
- { "DataFetcherImplAndroid", content::DataFetcherImplAndroid::Register },
- { "DateTimePickerAndroid", content::RegisterDateTimeChooserAndroid },
- { "DownloadControllerAndroidImpl",
- content::DownloadControllerAndroidImpl::RegisterDownloadController },
- { "InterstitialPageDelegateAndroid",
- content::InterstitialPageDelegateAndroid
- ::RegisterInterstitialPageDelegateAndroid },
- { "MediaResourceGetterImpl",
- content::MediaResourceGetterImpl::RegisterMediaResourceGetter },
- { "LoadUrlParams", content::RegisterLoadUrlParams },
- { "PowerSaveBlock", content::RegisterPowerSaveBlocker },
- { "RegisterImeAdapter", content::RegisterImeAdapter },
- { "SpeechRecognizerImplAndroid",
- content::SpeechRecognizerImplAndroid::RegisterSpeechRecognizer },
- { "TouchPoint", content::RegisterTouchPoint },
- { "TracingIntentHandler", content::RegisterTracingIntentHandler },
- { "VibrationMessageFilter", content::VibrationMessageFilter::Register },
- { "WebContentsObserverAndroid", content::RegisterWebContentsObserverAndroid },
- { "WebViewStatics", content::RegisterWebViewStatics },
-};
+ {"AndroidLocationApiAdapter",
+ content::AndroidLocationApiAdapter::RegisterGeolocationService},
+ {"AndroidBrowserProcess", content::RegisterAndroidBrowserProcess},
+ {"BrowserAccessibilityManager",
+ content::RegisterBrowserAccessibilityManager},
+ {"BrowserStartupConfiguration", content::RegisterBrowserStartupConfig},
+ {"ChildProcessLauncher", content::RegisterChildProcessLauncher},
+ {"ContentSettings", content::ContentSettings::RegisterContentSettings},
+ {"ContentViewRenderView",
+ content::ContentViewRenderView::RegisterContentViewRenderView},
+ {"ContentVideoView", content::ContentVideoView::RegisterContentVideoView},
+ {"ContentViewCore", content::RegisterContentViewCore},
+ {"DataFetcherImplAndroid", content::DataFetcherImplAndroid::Register},
+ {"DateTimePickerAndroid", content::RegisterDateTimeChooserAndroid},
+ {"DownloadControllerAndroidImpl",
+ content::DownloadControllerAndroidImpl::RegisterDownloadController},
+ {"InterstitialPageDelegateAndroid",
+ content::InterstitialPageDelegateAndroid::
+ RegisterInterstitialPageDelegateAndroid},
+ {"MediaResourceGetterImpl",
+ content::MediaResourceGetterImpl::RegisterMediaResourceGetter},
+ {"LoadUrlParams", content::RegisterLoadUrlParams},
+ {"PowerSaveBlock", content::RegisterPowerSaveBlocker},
+ {"RegisterImeAdapter", content::RegisterImeAdapter},
+ {"SpeechRecognizerImplAndroid",
+ content::SpeechRecognizerImplAndroid::RegisterSpeechRecognizer},
+ {"TouchPoint", content::RegisterTouchPoint},
+ {"TracingIntentHandler", content::RegisterTracingIntentHandler},
+ {"VibrationMessageFilter", content::VibrationMessageFilter::Register},
+ {"WebContentsObserverAndroid", content::RegisterWebContentsObserverAndroid},
+ {"WebViewStatics", content::RegisterWebViewStatics}, };
} // namespace
diff --git a/content/browser/android/browser_startup_config.cc b/content/browser/android/browser_startup_config.cc
new file mode 100644
index 0000000..3e74208
--- /dev/null
+++ b/content/browser/android/browser_startup_config.cc
@@ -0,0 +1,25 @@
+// Copyright 2013 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/browser_startup_config.h"
+
+#include "base/android/jni_android.h"
+#include "jni/BrowserStartupConfig_jni.h"
+
+namespace content {
+
+bool BrowserMayStartAsynchronously() {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ return Java_BrowserStartupConfig_browserMayStartAsynchonously(env);
+}
+
+void BrowserStartupComplete(int result) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_BrowserStartupConfig_browserStartupComplete(env, result);
+}
+
+bool RegisterBrowserStartupConfig(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+} // namespace content
diff --git a/content/browser/android/browser_startup_config.h b/content/browser/android/browser_startup_config.h
new file mode 100644
index 0000000..95575f2
--- /dev/null
+++ b/content/browser/android/browser_startup_config.h
@@ -0,0 +1,18 @@
+// Copyright 2013 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_BROWSER_BROWSER_STARTUP_CONFIGURATION_H_
+#define CONTENT_BROWSER_BROWSER_STARTUP_CONFIGURATION_H_
+
+#include <jni.h>
+
+namespace content {
+
+bool BrowserMayStartAsynchronously();
+void BrowserStartupComplete(int result);
+
+bool RegisterBrowserStartupConfig(JNIEnv* env);
+
+} // namespace content
+#endif // CONTENT_BROWSER_BROWSER_STARTUP_CONFIGURATION_H_
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index cc45bc2..9e02cc4 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -34,6 +34,7 @@
#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/browser/speech/speech_recognition_manager_impl.h"
+#include "content/browser/startup_task_runner.h"
#include "content/browser/tracing/trace_controller_impl.h"
#include "content/browser/webui/content_web_ui_controller_factory.h"
#include "content/browser/webui/url_data_manager.h"
@@ -60,6 +61,7 @@
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
+#include "content/browser/android/browser_startup_config.h"
#include "content/browser/android/surface_texture_peer_browser_impl.h"
#endif
@@ -299,7 +301,8 @@ BrowserMainLoop* BrowserMainLoop::GetInstance() {
BrowserMainLoop::BrowserMainLoop(const MainFunctionParams& parameters)
: parameters_(parameters),
parsed_command_line_(parameters.command_line),
- result_code_(RESULT_CODE_NORMAL_EXIT) {
+ result_code_(RESULT_CODE_NORMAL_EXIT),
+ created_threads_(false) {
DCHECK(!g_current_browser_main_loop);
g_current_browser_main_loop = this;
}
@@ -483,8 +486,7 @@ void BrowserMainLoop::MainMessageLoopStart() {
#endif
}
-void BrowserMainLoop::CreateThreads() {
- TRACE_EVENT0("startup", "BrowserMainLoop::CreateThreads")
+int BrowserMainLoop::PreCreateThreads() {
if (parts_) {
TRACE_EVENT0("startup",
@@ -509,9 +511,44 @@ void BrowserMainLoop::CreateThreads() {
if (parsed_command_line_.HasSwitch(switches::kSingleProcess))
RenderProcessHost::SetRunRendererInProcess(true);
#endif
+ return result_code_;
+}
- if (result_code_ > 0)
- return;
+void BrowserMainLoop::CreateStartupTasks() {
+ TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks")
+
+#if defined(OS_ANDROID)
+ scoped_refptr<StartupTaskRunner> task_runner =
+ new StartupTaskRunner(BrowserMayStartAsynchronously(),
+ 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());
+#endif
+ StartupTask pre_create_threads =
+ base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this));
+ task_runner->AddTask(pre_create_threads);
+
+ StartupTask create_threads =
+ base::Bind(&BrowserMainLoop::CreateThreads, base::Unretained(this));
+ task_runner->AddTask(create_threads);
+
+ StartupTask browser_thread_started = base::Bind(
+ &BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this));
+ 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);
+
+ task_runner->StartRunningTasks();
+}
+
+int BrowserMainLoop::CreateThreads() {
+ TRACE_EVENT0("startup", "BrowserMainLoop::CreateThreads");
base::Thread::Options default_options;
base::Thread::Options io_message_loop_options;
@@ -596,14 +633,11 @@ void BrowserMainLoop::CreateThreads() {
TRACE_EVENT_END0("startup", "BrowserMainLoop::CreateThreads:start");
}
+ created_threads_ = true;
+ return result_code_;
+}
-#if !defined(OS_IOS)
- indexed_db_thread_.reset(new base::Thread("IndexedDB"));
- indexed_db_thread_->Start();
-#endif
-
- BrowserThreadsStarted();
-
+int BrowserMainLoop::PreMainMessageLoopRun() {
if (parts_) {
TRACE_EVENT0("startup",
"BrowserMainLoop::CreateThreads:PreMainMessageLoopRun");
@@ -614,6 +648,7 @@ void BrowserMainLoop::CreateThreads() {
// Do not allow disk IO from the UI thread.
base::ThreadRestrictions::SetIOAllowed(false);
base::ThreadRestrictions::DisallowWaiting();
+ return result_code_;
}
void BrowserMainLoop::RunMainMessageLoopParts() {
@@ -630,6 +665,11 @@ void BrowserMainLoop::RunMainMessageLoopParts() {
}
void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
+
+ if (!created_threads_) {
+ // Called early, nothing to do
+ return;
+ }
// Teardown may start in PostMainMessageLoopRun, and during teardown we
// need to be able to perform IO.
base::ThreadRestrictions::SetIOAllowed(true);
@@ -765,8 +805,14 @@ void BrowserMainLoop::InitializeMainThread() {
new BrowserThreadImpl(BrowserThread::UI, base::MessageLoop::current()));
}
-void BrowserMainLoop::BrowserThreadsStarted() {
+int BrowserMainLoop::BrowserThreadsStarted() {
TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted")
+
+#if !defined(OS_IOS)
+ indexed_db_thread_.reset(new base::Thread("IndexedDB"));
+ indexed_db_thread_->Start();
+#endif
+
#if defined(OS_ANDROID)
// Up the priority of anything that touches with display tasks
// (this thread is UI thread, and io_thread_ is for IPCs).
@@ -844,6 +890,7 @@ void BrowserMainLoop::BrowserThreadsStarted() {
CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP));
}
#endif // !defined(OS_IOS)
+ return result_code_;
}
void BrowserMainLoop::InitializeToolkit() {
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 5c3608b..7b2879f 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -6,8 +6,10 @@
#define CONTENT_BROWSER_BROWSER_MAIN_LOOP_H_
#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "content/browser/browser_process_sub_thread.h"
+#include "content/public/browser/browser_main_runner.h"
class CommandLine;
@@ -65,8 +67,8 @@ class CONTENT_EXPORT BrowserMainLoop {
void InitializeToolkit();
void MainMessageLoopStart();
- // Create all secondary threads.
- void CreateThreads();
+ // Create the tasks we need to complete startup.
+ void CreateStartupTasks();
// Perform the default message loop run logic.
void RunMainMessageLoopParts();
@@ -94,8 +96,16 @@ class CONTENT_EXPORT BrowserMainLoop {
void InitializeMainThread();
+ // Called just before creating the threads
+ int PreCreateThreads();
+
+ // Create all secondary threads.
+ int CreateThreads();
+
// Called right after the browser threads have been started.
- void BrowserThreadsStarted();
+ int BrowserThreadsStarted();
+
+ int PreMainMessageLoopRun();
void MainMessageLoopRun();
@@ -103,6 +113,8 @@ class CONTENT_EXPORT BrowserMainLoop {
const MainFunctionParams& parameters_;
const CommandLine& parsed_command_line_;
int result_code_;
+ // True if the non-UI threads were created.
+ bool created_threads_;
// Members initialized in |MainMessageLoopStart()| ---------------------------
scoped_ptr<base::MessageLoop> main_message_loop_;
diff --git a/content/browser/browser_main_runner.cc b/content/browser/browser_main_runner.cc
index f50832a..cca1466 100644
--- a/content/browser/browser_main_runner.cc
+++ b/content/browser/browser_main_runner.cc
@@ -29,11 +29,7 @@ namespace content {
class BrowserMainRunnerImpl : public BrowserMainRunner {
public:
- BrowserMainRunnerImpl()
- : is_initialized_(false),
- is_shutdown_(false),
- created_threads_(false) {
- }
+ BrowserMainRunnerImpl() : is_initialized_(false), is_shutdown_(false) {}
virtual ~BrowserMainRunnerImpl() {
if (is_initialized_ && !is_shutdown_)
@@ -102,11 +98,10 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
#endif
ui::InitializeInputMethod();
- main_loop_->CreateThreads();
+ main_loop_->CreateStartupTasks();
int result_code = main_loop_->GetResultCode();
if (result_code > 0)
return result_code;
- created_threads_ = true;
// Return -1 to indicate no early termination.
return -1;
@@ -124,8 +119,7 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
DCHECK(!is_shutdown_);
g_exited_main_message_loop = true;
- if (created_threads_)
- main_loop_->ShutdownThreadsAndCleanUp();
+ main_loop_->ShutdownThreadsAndCleanUp();
ui::ShutdownInputMethod();
#if defined(OS_WIN)
@@ -146,9 +140,6 @@ class BrowserMainRunnerImpl : public BrowserMainRunner {
// True if the runner has been shut down.
bool is_shutdown_;
- // True if the non-UI threads were created.
- bool created_threads_;
-
scoped_ptr<NotificationServiceImpl> notification_service_;
scoped_ptr<BrowserMainLoop> main_loop_;
#if defined(OS_WIN)
diff --git a/content/browser/startup_task_runner.cc b/content/browser/startup_task_runner.cc
new file mode 100644
index 0000000..a7e730e
--- /dev/null
+++ b/content/browser/startup_task_runner.cc
@@ -0,0 +1,63 @@
+// Copyright 2013 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/startup_task_runner.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop.h"
+
+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) {}
+
+void StartupTaskRunner::AddTask(StartupTask& callback) {
+ task_list_.push_back(callback);
+}
+
+void StartupTaskRunner::StartRunningTasks() {
+ 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 (!startup_complete_callback_.is_null()) {
+ startup_complete_callback_.Run(result);
+ }
+ }
+}
+
+void StartupTaskRunner::WrappedTask() {
+ int result = task_list_.front().Run();
+ task_list_.pop_front();
+ if (result > 0 || task_list_.empty()) {
+ if (!startup_complete_callback_.is_null()) {
+ startup_complete_callback_.Run(result);
+ }
+ } else {
+ const base::Closure next_task =
+ base::Bind(&StartupTaskRunner::WrappedTask, 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
new file mode 100644
index 0000000..5f954ed
--- /dev/null
+++ b/content/browser/startup_task_runner.h
@@ -0,0 +1,66 @@
+// Copyright 2013 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_BROWSER_STARTUP_TASK_RUNNER_H_
+#define CONTENT_BROWSER_STARTUP_TASK_RUNNER_H_
+
+#include <list>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+
+#include "build/build_config.h"
+
+#include "content/public/browser/browser_main_runner.h"
+
+namespace content {
+
+// A startup task is a void function returning the status on completion.
+// a status of > 0 indicates a failure, and that no further startup tasks should
+// be run.
+typedef base::Callback<int(void)> StartupTask;
+
+// This class runs startup tasks. The tasks are either run immediately inline,
+// or are queued one at a time on the UI thread's message loop. If the events
+// are queued, UI events that are received during startup will be acted upon
+// between startup tasks. The motivation for this is that, on targets where the
+// UI is already started, it allows us to keep the UI responsive during startup.
+//
+// Note that this differs from a SingleThreadedTaskRunner in that there may be
+// no opportunity to handle UI events between the tasks of a
+// SingleThreadedTaskRunner.
+
+class CONTENT_EXPORT StartupTaskRunner
+ : public base::RefCounted<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,
+ scoped_refptr<base::SingleThreadTaskRunner> proxy);
+
+ // Add a task to the queue of startup tasks to be run.
+ virtual void AddTask(StartupTask& callback);
+
+ // Start running the tasks.
+ virtual void StartRunningTasks();
+
+ 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(StartupTaskRunner);
+};
+
+} // namespace content
+#endif // CONTENT_BROWSER_STARTUP_TASK_RUNNER_H_
diff --git a/content/browser/startup_task_runner_unittest.cc b/content/browser/startup_task_runner_unittest.cc
new file mode 100644
index 0000000..2efa79f
--- /dev/null
+++ b/content/browser/startup_task_runner_unittest.cc
@@ -0,0 +1,281 @@
+// Copyright 2013 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/startup_task_runner.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/run_loop.h"
+#include "base/task_runner.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+using base::Closure;
+using testing::_;
+using testing::Assign;
+using testing::Invoke;
+using testing::WithArg;
+
+bool observer_called = false;
+int observer_result;
+base::Closure task;
+
+// I couldn't get gMock's SaveArg to compile, hence had to save the argument
+// this way
+bool SaveTaskArg(const Closure& arg) {
+ task = arg;
+ return true;
+}
+
+void Observer(int result) {
+ observer_called = true;
+ observer_result = result;
+}
+
+class StartupTaskRunnerTest : public testing::Test {
+ public:
+
+ virtual void SetUp() {
+ last_task_ = 0;
+ observer_called = false;
+ }
+
+ int Task1() {
+ last_task_ = 1;
+ return 0;
+ }
+
+ int Task2() {
+ last_task_ = 2;
+ return 0;
+ }
+
+ int FailingTask() {
+ // Task returning failure
+ last_task_ = 3;
+ return 1;
+ }
+
+ int GetLastTask() { return last_task_; }
+
+ private:
+
+ int last_task_;
+};
+
+// We can't use the real message loop, even if we want to, since doing so on
+// Android requires a complex Java infrastructure. The test would have to built
+// as a content_shell test; but content_shell startup invokes the class we are
+// trying to test.
+//
+// The mocks are not directly in TaskRunnerProxy because reference counted
+// objects seem to confuse the mocking framework
+
+class MockTaskRunner {
+ public:
+ MOCK_METHOD3(
+ PostDelayedTask,
+ bool(const tracked_objects::Location&, const Closure&, base::TimeDelta));
+ MOCK_METHOD3(
+ PostNonNestableDelayedTask,
+ bool(const tracked_objects::Location&, const Closure&, base::TimeDelta));
+};
+
+class TaskRunnerProxy : public base::SingleThreadTaskRunner {
+ public:
+ TaskRunnerProxy(MockTaskRunner* mock) : mock_(mock) {}
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE { return true; }
+ virtual bool PostDelayedTask(const tracked_objects::Location& location,
+ const Closure& closure,
+ base::TimeDelta delta) OVERRIDE {
+ return mock_->PostDelayedTask(location, closure, delta);
+ }
+ virtual bool PostNonNestableDelayedTask(
+ const tracked_objects::Location& location,
+ const Closure& closure,
+ base::TimeDelta delta) OVERRIDE {
+ return mock_->PostNonNestableDelayedTask(location, closure, delta);
+ }
+
+ private:
+ MockTaskRunner* mock_;
+ virtual ~TaskRunnerProxy() {}
+};
+
+TEST_F(StartupTaskRunnerTest, SynchronousExecution) {
+ MockTaskRunner mock_runner;
+ scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
+
+ 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);
+
+ StartupTask task1 =
+ base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
+ runner->AddTask(task1);
+ EXPECT_EQ(GetLastTask(), 0);
+ StartupTask task2 =
+ base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
+ runner->AddTask(task2);
+
+ // Nothing should run until we tell them to.
+ EXPECT_EQ(GetLastTask(), 0);
+ runner->StartRunningTasks();
+
+ // On an immediate StartupTaskRunner the tasks should now all have run.
+ EXPECT_EQ(GetLastTask(), 2);
+
+ EXPECT_TRUE(observer_called);
+ EXPECT_EQ(observer_result, 0);
+}
+
+TEST_F(StartupTaskRunnerTest, NullObserver) {
+ MockTaskRunner mock_runner;
+ scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
+
+ 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);
+
+ StartupTask task1 =
+ base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
+ runner->AddTask(task1);
+ EXPECT_EQ(GetLastTask(), 0);
+ StartupTask task2 =
+ base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
+ runner->AddTask(task2);
+
+ // Nothing should run until we tell them to.
+ EXPECT_EQ(GetLastTask(), 0);
+ runner->StartRunningTasks();
+
+ // On an immediate StartupTaskRunner the tasks should now all have run.
+ EXPECT_EQ(GetLastTask(), 2);
+
+ EXPECT_FALSE(observer_called);
+}
+
+TEST_F(StartupTaskRunnerTest, SynchronousExecutionFailedTask) {
+ MockTaskRunner mock_runner;
+ scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
+
+ 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);
+
+ StartupTask task3 =
+ base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this));
+ runner->AddTask(task3);
+ EXPECT_EQ(GetLastTask(), 0);
+ StartupTask task2 =
+ base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
+ runner->AddTask(task2);
+
+ // Nothing should run until we tell them to.
+ EXPECT_EQ(GetLastTask(), 0);
+ runner->StartRunningTasks();
+
+ // Only the first task should have run, since it failed
+ EXPECT_EQ(GetLastTask(), 3);
+
+ EXPECT_TRUE(observer_called);
+ EXPECT_EQ(observer_result, 1);
+}
+
+TEST_F(StartupTaskRunnerTest, AsynchronousExecution) {
+
+ MockTaskRunner mock_runner;
+ scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
+
+ EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
+ EXPECT_CALL(
+ mock_runner,
+ PostNonNestableDelayedTask(_, _, base::TimeDelta::FromMilliseconds(0)))
+ .Times(testing::Between(2, 3))
+ .WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg)));
+
+ scoped_refptr<StartupTaskRunner> runner =
+ new StartupTaskRunner(true, base::Bind(&Observer), proxy);
+
+ StartupTask task1 =
+ base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
+ runner->AddTask(task1);
+ StartupTask task2 =
+ base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
+ runner->AddTask(task2);
+
+ // Nothing should run until we tell them to.
+ EXPECT_EQ(GetLastTask(), 0);
+ runner->StartRunningTasks();
+
+ // No tasks should have run yet, since we the message loop hasn't run.
+ EXPECT_EQ(GetLastTask(), 0);
+
+ // Fake the actual message loop. Each time a task is run a new task should
+ // be added to the queue, hence updating "task". The loop should actually run
+ // at most 3 times (once for each task plus possibly once for the observer),
+ // the "4" is a backstop.
+ for (int i = 0; i < 4 && !observer_called; i++) {
+ task.Run();
+ EXPECT_EQ(i + 1, GetLastTask());
+ }
+ EXPECT_TRUE(observer_called);
+ EXPECT_EQ(observer_result, 0);
+}
+
+TEST_F(StartupTaskRunnerTest, AsynchronousExecutionFailedTask) {
+
+ MockTaskRunner mock_runner;
+ scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
+
+ EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
+ EXPECT_CALL(
+ mock_runner,
+ PostNonNestableDelayedTask(_, _, base::TimeDelta::FromMilliseconds(0)))
+ .Times(testing::Between(1, 2))
+ .WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg)));
+
+ scoped_refptr<StartupTaskRunner> runner =
+ new StartupTaskRunner(true, base::Bind(&Observer), proxy);
+
+ StartupTask task3 =
+ base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this));
+ runner->AddTask(task3);
+ StartupTask task2 =
+ base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
+ runner->AddTask(task2);
+
+ // Nothing should run until we tell them to.
+ EXPECT_EQ(GetLastTask(), 0);
+ runner->StartRunningTasks();
+
+ // No tasks should have run yet, since we the message loop hasn't run.
+ EXPECT_EQ(GetLastTask(), 0);
+
+ // Fake the actual message loop. Each time a task is run a new task should
+ // be added to the queue, hence updating "task". The loop should actually run
+ // at most twice (once for the failed task plus possibly once for the
+ // observer), the "4" is a backstop.
+ for (int i = 0; i < 4 && !observer_called; i++) {
+ task.Run();
+ }
+ EXPECT_EQ(GetLastTask(), 3);
+
+ EXPECT_TRUE(observer_called);
+ EXPECT_EQ(observer_result, 1);
+}
+} // namespace
+} // namespace content
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 6d6581d..d7120cb 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -237,6 +237,8 @@
'browser/android/browser_jni_registrar.h',
'browser/android/browser_media_player_manager.cc',
'browser/android/browser_media_player_manager.h',
+ 'browser/android/browser_startup_config.cc',
+ 'browser/android/browser_startup_config.h',
'browser/android/child_process_launcher_android.cc',
'browser/android/child_process_launcher_android.h',
'browser/android/content_settings.cc',
@@ -1046,6 +1048,8 @@
'browser/ssl/ssl_policy.h',
'browser/ssl/ssl_request_info.cc',
'browser/ssl/ssl_request_info.h',
+ 'browser/startup_task_runner.cc',
+ 'browser/startup_task_runner.h',
'browser/streams/stream.cc',
'browser/streams/stream.h',
'browser/streams/stream_context.cc',
diff --git a/content/content_jni.gypi b/content/content_jni.gypi
index fe10d80..0ea4ad4 100644
--- a/content/content_jni.gypi
+++ b/content/content_jni.gypi
@@ -13,6 +13,7 @@
'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/BrowserStartupConfig.java',
'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java',
'public/android/java/src/org/chromium/content/browser/ContentSettings.java',
'public/android/java/src/org/chromium/content/browser/ContentVideoView.java',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 1425e81..b5c41e5 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -369,6 +369,7 @@
'browser/speech/google_streaming_remote_engine_unittest.cc',
'browser/speech/speech_recognizer_impl_unittest.cc',
'browser/ssl/ssl_host_state_unittest.cc',
+ 'browser/startup_task_runner_unittest.cc',
'browser/storage_partition_impl_unittest.cc',
'browser/storage_partition_impl_map_unittest.cc',
'browser/streams/stream_unittest.cc',
diff --git a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupConfig.java b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupConfig.java
new file mode 100644
index 0000000..f784c7a
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupConfig.java
@@ -0,0 +1,43 @@
+// Copyright 2013 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.
+
+package org.chromium.content.browser;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+/**
+ * This class controls how C++ browser main loop is run.
+ */
+@JNINamespace("content")
+public class BrowserStartupConfig {
+ public interface StartupCallback {
+ void run(int startupResult);
+ }
+
+ private static boolean sBrowserMayStartAsynchronously = false;
+ private static StartupCallback sBrowserStartupCompleteCallback = null;
+
+ @CalledByNative
+ private static boolean browserMayStartAsynchonously() {
+ return sBrowserMayStartAsynchronously;
+ }
+
+ @CalledByNative
+ private static void browserStartupComplete(int result) {
+ if(sBrowserStartupCompleteCallback != null) {
+ sBrowserStartupCompleteCallback.run(result);
+ }
+ }
+
+ /**
+ * Set browser to start asynchronously. May only be called before contentMain.start(). If it
+ * has been called then contentMain.start() will queue up a series of UI tasks to complete
+ * browser initialization.
+ * @param browserStartupCompleteCallback If not null called when browser startup is complete.
+ */
+ public static void setAsync(StartupCallback browserStartupCompleteCallback) {
+ sBrowserMayStartAsynchronously = true;
+ sBrowserStartupCompleteCallback = browserStartupCompleteCallback;
+ }
+}
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 9e37370..e24835b 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,7 @@ 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.ContentVideoView;
+import org.chromium.content.browser.BrowserStartupConfig;
import org.chromium.content.browser.ContentVideoViewClient;
import org.chromium.content.browser.ContentView;
import org.chromium.content.browser.ContentViewClient;
@@ -92,26 +92,45 @@ public class ContentShellActivity extends ChromiumActivity {
if (!TextUtils.isEmpty(startupUrl)) {
mShellManager.setStartupUrl(Shell.sanitizeUrl(startupUrl));
}
+
+ BrowserStartupConfig.setAsync(new BrowserStartupConfig.StartupCallback() {
+
+ @Override
+ public void run(int startupResult) {
+ if (startupResult > 0) {
+ // TODO: Show error message.
+ Log.e(TAG, "ContentView initialization failed.");
+ finish();
+ } else {
+ finishInitialization();
+ }
+ }
+ });
+
if (!AndroidBrowserProcess.init(this, AndroidBrowserProcess.MAX_RENDERERS_LIMIT)) {
String shellUrl = ShellManager.DEFAULT_SHELL_URL;
if (savedInstanceState != null
- && savedInstanceState.containsKey(ACTIVE_SHELL_URL_KEY)) {
+ && savedInstanceState.containsKey(ACTIVE_SHELL_URL_KEY)) {
shellUrl = savedInstanceState.getString(ACTIVE_SHELL_URL_KEY);
}
mShellManager.launchShell(shellUrl);
+ finishInitialization();
}
- getActiveContentView().setContentViewClient(new ContentViewClient() {
- @Override
- public ContentVideoViewClient getContentVideoViewClient() {
- return new ActivityContentVideoViewClient(ContentShellActivity.this);
- }
- });
} catch (ProcessInitException e) {
Log.e(TAG, "ContentView initialization failed.", e);
finish();
}
}
+ private void finishInitialization() {
+ getActiveContentView().setContentViewClient(new ContentViewClient() {
+ @Override
+ public ContentVideoViewClient getContentVideoViewClient() {
+ return new ActivityContentVideoViewClient(ContentShellActivity.this);
+ }
+ });
+ }
+
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
diff --git a/content/shell/shell_browser_main_parts.h b/content/shell/shell_browser_main_parts.h
index 8ba4c1f..25a4449 100644
--- a/content/shell/shell_browser_main_parts.h
+++ b/content/shell/shell_browser_main_parts.h
@@ -8,6 +8,7 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "content/public/browser/browser_main_parts.h"
+#include "content/public/common/main_function_params.h"
namespace base {
class Thread;
@@ -22,7 +23,6 @@ namespace content {
class ShellBrowserContext;
class ShellDevToolsDelegate;
class ShellPluginServiceFilter;
-struct MainFunctionParams;
class ShellBrowserMainParts : public BrowserMainParts {
public:
@@ -54,7 +54,7 @@ class ShellBrowserMainParts : public BrowserMainParts {
scoped_ptr<ShellBrowserContext> off_the_record_browser_context_;
// For running content_browsertests.
- const MainFunctionParams& parameters_;
+ const MainFunctionParams parameters_;
bool run_message_loop_;
scoped_ptr<ShellDevToolsDelegate> devtools_delegate_;