diff options
Diffstat (limited to 'ios/web/app')
-rw-r--r-- | ios/web/app/web_main.mm | 19 | ||||
-rw-r--r-- | ios/web/app/web_main_loop.h | 99 | ||||
-rw-r--r-- | ios/web/app/web_main_loop.mm | 278 | ||||
-rw-r--r-- | ios/web/app/web_main_runner.h | 29 | ||||
-rw-r--r-- | ios/web/app/web_main_runner.mm | 133 |
5 files changed, 558 insertions, 0 deletions
diff --git a/ios/web/app/web_main.mm b/ios/web/app/web_main.mm new file mode 100644 index 0000000..5e5aa2b --- /dev/null +++ b/ios/web/app/web_main.mm @@ -0,0 +1,19 @@ +// Copyright 2014 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 "ios/web/app/web_main_runner.h" +#include "ios/web/public/app/web_main.h" + +namespace web { + +WebMain::WebMain(const WebMainParams& params) { + web_main_runner_.reset(WebMainRunner::Create()); + web_main_runner_->Initialize(params); +} + +WebMain::~WebMain() { + web_main_runner_->ShutDown(); +} + +} // namespace web diff --git a/ios/web/app/web_main_loop.h b/ios/web/app/web_main_loop.h new file mode 100644 index 0000000..8fd9178 --- /dev/null +++ b/ios/web/app/web_main_loop.h @@ -0,0 +1,99 @@ +// Copyright 2014 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 IOS_WEB_APP_WEB_MAIN_LOOP_H_ +#define IOS_WEB_APP_WEB_MAIN_LOOP_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +class CommandLine; +class FilePath; +class MessageLoop; +class PowerMonitor; +class SystemMonitor; +} // namespace base + +namespace net { +class NetworkChangeNotifier; +} // namespace net + +namespace web { +class CookieNotificationBridge; +class WebMainParts; +class WebThreadImpl; + +// Implements the main web loop stages called from WebMainRunner. +// See comments in web_main_parts.h for additional info. +class WebMainLoop { + public: + explicit WebMainLoop(); + virtual ~WebMainLoop(); + + void Init(); + + void EarlyInitialization(); + void MainMessageLoopStart(); + + // Creates and starts running the tasks needed to complete startup. + void CreateStartupTasks(); + + // Performs the shutdown sequence, starting with PostMainMessageLoopRun + // through stopping threads to PostDestroyThreads. + void ShutdownThreadsAndCleanUp(); + + int GetResultCode() const { return result_code_; } + + private: + void InitializeMainThread(); + + // Called just before creating the threads + int PreCreateThreads(); + + // Creates all secondary threads. + int CreateThreads(); + + // Called right after the web threads have been started. + int WebThreadsStarted(); + + // Called just before attaching to the main message loop. + int PreMainMessageLoopRun(); + + // Members initialized on construction --------------------------------------- + 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_; + scoped_ptr<base::SystemMonitor> system_monitor_; + scoped_ptr<base::PowerMonitor> power_monitor_; + scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; + + // Destroy parts_ before main_message_loop_ (required) and before other + // classes constructed in web (but after main_thread_). + scoped_ptr<WebMainParts> parts_; + + // Members initialized in |InitializeMainThread()| --------------------------- + // This must get destroyed before other threads that are created in parts_. + scoped_ptr<WebThreadImpl> main_thread_; + + // Members initialized in |RunMainMessageLoopParts()| ------------------------ + scoped_ptr<WebThreadImpl> db_thread_; + scoped_ptr<WebThreadImpl> file_user_blocking_thread_; + scoped_ptr<WebThreadImpl> file_thread_; + scoped_ptr<WebThreadImpl> cache_thread_; + scoped_ptr<WebThreadImpl> io_thread_; + + // Members initialized in |WebThreadsStarted()| -------------------------- + scoped_ptr<CookieNotificationBridge> cookie_notification_bridge_; + + DISALLOW_COPY_AND_ASSIGN(WebMainLoop); +}; + +} // namespace web + +#endif // IOS_WEB_APP_WEB_MAIN_LOOP_H_ diff --git a/ios/web/app/web_main_loop.mm b/ios/web/app/web_main_loop.mm new file mode 100644 index 0000000..b3300d4 --- /dev/null +++ b/ios/web/app/web_main_loop.mm @@ -0,0 +1,278 @@ +// Copyright 2014 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 "ios/web/app/web_main_loop.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" +#include "base/path_service.h" +#include "base/power_monitor/power_monitor.h" +#include "base/power_monitor/power_monitor_device_source.h" +#include "base/process/process_metrics.h" +#include "base/system_monitor/system_monitor.h" +#include "base/threading/thread_restrictions.h" +#include "crypto/nss_util.h" +#include "ios/web/net/cookie_notification_bridge.h" +#include "ios/web/public/app/web_main_parts.h" +#include "ios/web/public/web_client.h" +#include "ios/web/web_thread_impl.h" +#include "net/base/network_change_notifier.h" + +namespace web { + +// The currently-running WebMainLoop. There can be one or zero. +// TODO(rohitrao): Desktop uses this to implement +// ImmediateShutdownAndExitProcess. If we don't need that functionality, we can +// remove this. +WebMainLoop* g_current_web_main_loop = nullptr; + +WebMainLoop::WebMainLoop() : result_code_(0), created_threads_(false) { + DCHECK(!g_current_web_main_loop); + g_current_web_main_loop = this; +} + +WebMainLoop::~WebMainLoop() { + DCHECK_EQ(this, g_current_web_main_loop); + g_current_web_main_loop = nullptr; +} + +void WebMainLoop::Init() { + parts_.reset(web::GetWebClient()->CreateWebMainParts()); +} + +void WebMainLoop::EarlyInitialization() { + if (parts_) { + parts_->PreEarlyInitialization(); + } + +#if !defined(USE_OPENSSL) + // We want to be sure to init NSPR on the main thread. + crypto::EnsureNSPRInit(); +#endif // !defined(USE_OPENSSL) + + if (parts_) { + parts_->PostEarlyInitialization(); + } +} + +void WebMainLoop::MainMessageLoopStart() { + if (parts_) { + parts_->PreMainMessageLoopStart(); + } + + // Create a MessageLoop if one does not already exist for the current thread. + if (!base::MessageLoop::current()) { + main_message_loop_.reset(new base::MessageLoopForUI); + } + // Note: In Chrome, Attach() is called in + // ChromeBrowserMainPartsIOS::PreMainMessageLoopStart(). + base::MessageLoopForUI::current()->Attach(); + + InitializeMainThread(); + +#if 0 + // TODO(droger): SystemMonitor is not working properly on iOS. + // See http://crbug.com/228014. + system_monitor_.reset(new base::SystemMonitor); +#endif + // TODO(rohitrao): Do we need PowerMonitor on iOS, or can we get rid of it? + scoped_ptr<base::PowerMonitorSource> power_monitor_source( + new base::PowerMonitorDeviceSource()); + power_monitor_.reset(new base::PowerMonitor(power_monitor_source.Pass())); + network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); + + if (parts_) { + parts_->PostMainMessageLoopStart(); + } +} + +void WebMainLoop::CreateStartupTasks() { + int result = 0; + result = PreCreateThreads(); + if (result > 0) + return; + + result = CreateThreads(); + if (result > 0) + return; + + result = WebThreadsStarted(); + if (result > 0) + return; + + result = PreMainMessageLoopRun(); + if (result > 0) + return; +} + +int WebMainLoop::PreCreateThreads() { + if (parts_) { + result_code_ = parts_->PreCreateThreads(); + } + + return result_code_; +} + +int WebMainLoop::CreateThreads() { + base::Thread::Options default_options; + base::Thread::Options io_message_loop_options; + io_message_loop_options.message_loop_type = base::MessageLoop::TYPE_IO; + base::Thread::Options ui_message_loop_options; + ui_message_loop_options.message_loop_type = base::MessageLoop::TYPE_UI; + + // Start threads in the order they occur in the WebThread::ID + // enumeration, except for WebThread::UI which is the main + // thread. + // + // Must be size_t so we can increment it. + for (size_t thread_id = WebThread::UI + 1; thread_id < WebThread::ID_COUNT; + ++thread_id) { + scoped_ptr<WebThreadImpl>* thread_to_start = nullptr; + base::Thread::Options* options = &default_options; + + switch (thread_id) { + // TODO(rohitrao): We probably do not need all of these threads. Remove + // the ones that serve no purpose. http://crbug.com/365909 + case WebThread::DB: + thread_to_start = &db_thread_; + break; + case WebThread::FILE_USER_BLOCKING: + thread_to_start = &file_user_blocking_thread_; + break; + case WebThread::FILE: + thread_to_start = &file_thread_; + options = &io_message_loop_options; + break; + case WebThread::CACHE: + thread_to_start = &cache_thread_; + options = &io_message_loop_options; + break; + case WebThread::IO: + thread_to_start = &io_thread_; + options = &io_message_loop_options; + break; + case WebThread::UI: + case WebThread::ID_COUNT: + default: + NOTREACHED(); + break; + } + + WebThread::ID id = static_cast<WebThread::ID>(thread_id); + + if (thread_to_start) { + (*thread_to_start).reset(new WebThreadImpl(id)); + (*thread_to_start)->StartWithOptions(*options); + } else { + NOTREACHED(); + } + } + created_threads_ = true; + return result_code_; +} + +int WebMainLoop::PreMainMessageLoopRun() { + if (parts_) { + parts_->PreMainMessageLoopRun(); + } + + // If the UI thread blocks, the whole UI is unresponsive. + // Do not allow disk IO from the UI thread. + base::ThreadRestrictions::SetIOAllowed(false); + base::ThreadRestrictions::DisallowWaiting(); + return result_code_; +} + +void WebMainLoop::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); + WebThread::PostTask( + WebThread::IO, FROM_HERE, + base::Bind(base::IgnoreResult(&base::ThreadRestrictions::SetIOAllowed), + true)); + + if (parts_) { + parts_->PostMainMessageLoopRun(); + } + + // Must be size_t so we can subtract from it. + for (size_t thread_id = WebThread::ID_COUNT - 1; + thread_id >= (WebThread::UI + 1); --thread_id) { + // Find the thread object we want to stop. Looping over all valid + // WebThread IDs and DCHECKing on a missing case in the switch + // statement helps avoid a mismatch between this code and the + // WebThread::ID enumeration. + // + // The destruction order is the reverse order of occurrence in the + // WebThread::ID list. The rationale for the order is as + // follows (need to be filled in a bit): + // + // + // - The IO thread is the only user of the CACHE thread. + // + // - (Not sure why DB stops last.) + switch (thread_id) { + case WebThread::DB: + db_thread_.reset(); + break; + case WebThread::FILE_USER_BLOCKING: + file_user_blocking_thread_.reset(); + break; + case WebThread::FILE: + file_thread_.reset(); + break; + case WebThread::CACHE: + cache_thread_.reset(); + break; + case WebThread::IO: + io_thread_.reset(); + break; + case WebThread::UI: + case WebThread::ID_COUNT: + default: + NOTREACHED(); + break; + } + } + + // Close the blocking I/O pool after the other threads. Other threads such + // as the I/O thread may need to schedule work like closing files or flushing + // data during shutdown, so the blocking pool needs to be available. There + // may also be slow operations pending that will block shutdown, so closing + // it here (which will block until required operations are complete) gives + // more head start for those operations to finish. + WebThreadImpl::ShutdownThreadPool(); + + if (parts_) { + parts_->PostDestroyThreads(); + } +} + +void WebMainLoop::InitializeMainThread() { + const char* kThreadName = "CrWebMain"; + base::PlatformThread::SetName(kThreadName); + if (main_message_loop_) { + main_message_loop_->set_thread_name(kThreadName); + } + + // Register the main thread by instantiating it, but don't call any methods. + main_thread_.reset( + new WebThreadImpl(WebThread::UI, base::MessageLoop::current())); +} + +int WebMainLoop::WebThreadsStarted() { + cookie_notification_bridge_.reset(new CookieNotificationBridge); + return result_code_; +} + +} // namespace web diff --git a/ios/web/app/web_main_runner.h b/ios/web/app/web_main_runner.h new file mode 100644 index 0000000..c0c3c6f --- /dev/null +++ b/ios/web/app/web_main_runner.h @@ -0,0 +1,29 @@ +// Copyright 2014 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 IOS_WEB_APP_WEB_MAIN_RUNNER_H_ +#define IOS_WEB_APP_WEB_MAIN_RUNNER_H_ + +#include "ios/web/public/app/web_main.h" + +namespace web { + +// This class is responsible for web initialization and shutdown. +class WebMainRunner { + public: + virtual ~WebMainRunner() {} + + // Create a new WebMainRunner object. + static WebMainRunner* Create(); + + // Initialize all necessary web state. + virtual int Initialize(const WebMainParams& params) = 0; + + // Shut down the web state. + virtual void ShutDown() = 0; +}; + +} // namespace web + +#endif // IOS_WEB_APP_WEB_MAIN_RUNNER_H_ diff --git a/ios/web/app/web_main_runner.mm b/ios/web/app/web_main_runner.mm new file mode 100644 index 0000000..ff50552 --- /dev/null +++ b/ios/web/app/web_main_runner.mm @@ -0,0 +1,133 @@ +// Copyright 2014 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 "ios/web/app/web_main_runner.h" + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/i18n/icu_util.h" +#include "base/logging.h" +#include "base/metrics/statistics_recorder.h" +#include "ios/web/app/web_main_loop.h" +#include "ios/web/public/web_client.h" +#include "ui/base/ui_base_paths.h" + +namespace web { + +class WebMainRunnerImpl : public WebMainRunner { + public: + WebMainRunnerImpl() + : is_initialized_(false), + is_shutdown_(false), + completed_basic_startup_(false), + delegate_(nullptr) {} + + ~WebMainRunnerImpl() override { + if (is_initialized_ && !is_shutdown_) { + ShutDown(); + } + } + + int Initialize(const WebMainParams& params) override { + //////////////////////////////////////////////////////////////////////// + // ContentMainRunnerImpl::Initialize() + // + is_initialized_ = true; + delegate_ = params.delegate; + + // TODO(rohitrao): Chrome for iOS initializes this in main(), because it's + // needed for breakpad. Are we really going to require that all embedders + // initialize an AtExitManager in main()? + exit_manager_.reset(new base::AtExitManager); + + // There is no way to pass commandline flags to process on iOS, so the + // CommandLine is always initialized empty. Embedders can add switches in + // |BasicStartupComplete|. + base::CommandLine::Init(0, nullptr); + if (delegate_) { + delegate_->BasicStartupComplete(); + } + completed_basic_startup_ = true; + + // TODO(rohitrao): Should we instead require that all embedders call + // SetWebClient()? + if (!GetWebClient()) + SetWebClient(&empty_web_client_); + +#if defined(USE_NSS) + crypto::EarlySetupForNSSInit(); +#endif + + // TODO(rohitrao): Desktop calls content::RegisterContentSchemes(true) here. + // Do we need similar scheme registration on iOS? + ui::RegisterPathProvider(); + + CHECK(base::i18n::InitializeICU()); + + //////////////////////////////////////////////////////////// + // BrowserMainRunnerImpl::Initialize() + base::StatisticsRecorder::Initialize(); + + main_loop_.reset(new WebMainLoop()); + main_loop_->Init(); + main_loop_->EarlyInitialization(); + main_loop_->MainMessageLoopStart(); + main_loop_->CreateStartupTasks(); + int result_code = main_loop_->GetResultCode(); + if (result_code > 0) + return result_code; + + // Return -1 to indicate no early termination. + return -1; + } + + void ShutDown() override { + //////////////////////////////////////////////////////////////////// + // BrowserMainRunner::Shutdown() + // + DCHECK(is_initialized_); + DCHECK(!is_shutdown_); + main_loop_->ShutdownThreadsAndCleanUp(); + main_loop_.reset(nullptr); + + //////////////////////////////////////////////////////////////////// + // ContentMainRunner::Shutdown() + // + if (completed_basic_startup_ && delegate_) { + delegate_->ProcessExiting(); + } + + exit_manager_.reset(nullptr); + delegate_ = nullptr; + is_shutdown_ = true; + } + + protected: + // True if we have started to initialize the runner. + bool is_initialized_; + + // True if the runner has been shut down. + bool is_shutdown_; + + // True if basic startup was completed. + bool completed_basic_startup_; + + // The delegate will outlive this object. + WebMainDelegate* delegate_; + + // Used if the embedder doesn't set one. + WebClient empty_web_client_; + + scoped_ptr<base::AtExitManager> exit_manager_; + scoped_ptr<WebMainLoop> main_loop_; + + DISALLOW_COPY_AND_ASSIGN(WebMainRunnerImpl); +}; + +// static +WebMainRunner* WebMainRunner::Create() { + return new WebMainRunnerImpl(); +} + +} // namespace web |