diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/browser.cc | 4 | ||||
-rw-r--r-- | chrome/browser/service/service_process_control.cc | 212 | ||||
-rw-r--r-- | chrome/browser/service/service_process_control.h | 116 | ||||
-rw-r--r-- | chrome/browser/service/service_process_control_browsertest.cc | 70 | ||||
-rw-r--r-- | chrome/browser/service/service_process_control_manager.cc | 45 | ||||
-rw-r--r-- | chrome/browser/service/service_process_control_manager.h | 45 |
6 files changed, 492 insertions, 0 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index 8fdd799..a5f65d2 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -66,6 +66,7 @@ #endif #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/site_instance.h" +#include "chrome/browser/service/service_process_control_manager.h" #include "chrome/browser/sessions/session_service.h" #include "chrome/browser/sessions/session_types.h" #include "chrome/browser/status_bubble.h" @@ -844,6 +845,9 @@ void Browser::OnWindowClosing() { Source<Browser>(this), Details<bool>(&exiting)); + // Shutdown all IPC channels to service processes. + ServiceProcessControlManager::instance()->Shutdown(); + CloseAllTabs(); } diff --git a/chrome/browser/service/service_process_control.cc b/chrome/browser/service/service_process_control.cc new file mode 100644 index 0000000..6cf649b --- /dev/null +++ b/chrome/browser/service/service_process_control.cc @@ -0,0 +1,212 @@ +// Copyright (c) 2010 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 "chrome/browser/service/service_process_control.h" + +#include "base/command_line.h" +#include "base/process_util.h" +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/io_thread.h" +#include "chrome/common/child_process_host.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/service_messages.h" +#include "chrome/common/service_process_util.h" + +// ServiceProcessControl::Launcher implementation. +// This class is responsible for launching the service process on the +// PROCESS_LAUNCHER thread. +class ServiceProcessControl::Launcher + : public base::RefCountedThreadSafe<ServiceProcessControl::Launcher> { + public: + Launcher(ServiceProcessControl* process, CommandLine* cmd_line) + : process_(process), + cmd_line_(cmd_line), + launched_(false) { + } + + // Execute the command line to start the process asynchronously. + // After the comamnd is executed |task| is called with the process handle on + // the UI thread. + void Run(Task* task) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + ChromeThread::PostTask(ChromeThread::PROCESS_LAUNCHER, FROM_HERE, + NewRunnableMethod(this, &Launcher::DoRun, task)); + } + + bool launched() const { return launched_; } + + private: + void DoRun(Task* task) { + launched_ = base::LaunchApp(*cmd_line_.get(), false, true, NULL); + + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod(this, &Launcher::DoDetectLaunched, task)); + } + + void DoDetectLaunched(Task* task) { + // TODO(hclam): We need to improve the method we are using to connect to + // the service process. The approach we are using here is to check for + // the existence of the service process lock file created after the service + // process is fully launched. + if (CheckServiceProcessRunning(kServiceProcessCloudPrint)) { + // After the process is launched we listen on the file system for the + // service process lock file to detect the service process has launched. + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &Launcher::Notify, task)); + return; + } + + // If the service process is not launched yet then check again in 2 seconds. + const int kDetectLaunchRetry = 2000; + ChromeThread::PostDelayedTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod(this, &Launcher::DoDetectLaunched, task), + kDetectLaunchRetry); + } + + void Notify(Task* task) { + task->Run(); + delete task; + } + + ServiceProcessControl* process_; + scoped_ptr<CommandLine> cmd_line_; + bool launched_; +}; + +// ServiceProcessControl implementation. +ServiceProcessControl::ServiceProcessControl(Profile* profile, + ServiceProcessType type) + : profile_(profile), + type_(type), + message_handler_(NULL) { +} + +ServiceProcessControl::~ServiceProcessControl() { +} + +void ServiceProcessControl::Connect(Task* task) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + if (channel_.get()) { + task->Run(); + delete task; + return; + } + + // Saves the task. + connect_done_task_.reset(task); + ConnectInternal(); +} + +void ServiceProcessControl::ConnectInternal() { + LOG(INFO) << "Connecting to Service Process IPC Server"; + // Run the IPC channel on the shared IO thread. + base::Thread* io_thread = g_browser_process->io_thread(); + + // TODO(hclam): Determine the the channel id from profile and type. + const std::string channel_id = GetServiceProcessChannelName(type_); + channel_.reset( + new IPC::SyncChannel(channel_id, IPC::Channel::MODE_CLIENT, this, NULL, + io_thread->message_loop(), true, + g_browser_process->shutdown_event())); + channel_->set_sync_messages_with_no_timeout_allowed(false); +} + +void ServiceProcessControl::Launch(Task* task) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + if (channel_.get()) { + task->Run(); + delete task; + return; + } + + // A service process should have a different mechanism for starting, but now + // we start it as if it is a child process. + FilePath exe_path = ChildProcessHost::GetChildPath(true); + if (exe_path.empty()) { + NOTREACHED() << "Unable to get service process binary name."; + } + + CommandLine* cmd_line = new CommandLine(exe_path); + cmd_line->AppendSwitchASCII(switches::kProcessType, + switches::kServiceProcess); + + const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + FilePath user_data_dir = + browser_command_line.GetSwitchValuePath(switches::kUserDataDir); + if (!user_data_dir.empty()) + cmd_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir); + + std::string logging_level = browser_command_line.GetSwitchValueASCII( + switches::kLoggingLevel); + if (!logging_level.empty()) + cmd_line->AppendSwitchASCII(switches::kLoggingLevel, logging_level); + + // And then start the process asynchronously. + launcher_ = new Launcher(this, cmd_line); + launcher_->Run( + NewRunnableMethod(this, &ServiceProcessControl::OnProcessLaunched, task)); +} + +void ServiceProcessControl::OnProcessLaunched(Task* task) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + if (launcher_->launched()) { + // Now use the launch task as the connect task. + connect_done_task_.reset(task); + + // After we have successfully created the service process we try to connect + // to it. The launch task is transfered to a connect task. + ConnectInternal(); + } else { + // If we don't have process handle that means launching the service process + // has failed. + task->Run(); + delete task; + } + + // We don't need the launcher anymore. + launcher_ = NULL; +} + +void ServiceProcessControl::OnMessageReceived(const IPC::Message& message) { + if (!message_handler_) + return; + + if (message.type() == ServiceHostMsg_GoodDay::ID) + message_handler_->OnGoodDay(); +} + +void ServiceProcessControl::OnChannelConnected(int32 peer_pid) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + connect_done_task_->Run(); + connect_done_task_.reset(); +} + +void ServiceProcessControl::OnChannelError() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + channel_.reset(); + connect_done_task_->Run(); + connect_done_task_.reset(); +} + +bool ServiceProcessControl::Send(IPC::Message* message) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + return channel_->Send(message); +} + +bool ServiceProcessControl::SendHello() { + return Send(new ServiceMsg_Hello()); +} + +bool ServiceProcessControl::Shutdown() { + bool ret = Send(new ServiceMsg_Shutdown()); + channel_.reset(); + return ret; +} + +DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcessControl); diff --git a/chrome/browser/service/service_process_control.h b/chrome/browser/service/service_process_control.h new file mode 100644 index 0000000..469b17d --- /dev/null +++ b/chrome/browser/service/service_process_control.h @@ -0,0 +1,116 @@ +// Copyright (c) 2010 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 CHROME_BROWSER_SERVICE_SERVICE_PROCESS_CONTROL_H_ +#define CHROME_BROWSER_SERVICE_SERVICE_PROCESS_CONTROL_H_ + +#include <queue> + +#include "base/id_map.h" +#include "base/process.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "chrome/common/service_process_type.h" +#include "ipc/ipc_sync_channel.h" + +class Profile; + +// A ServiceProcessControl works as a portal between the service process and +// the browser process. +// +// It is used to start and terminate the service process. It is also used +// to send and receive IPC messages from the service process. +// +// THREADING +// +// This class is accessed on the UI thread through some UI actions. It then +// talks to the IPC channel on the IO thread. +class ServiceProcessControl : public IPC::Channel::Sender, + public IPC::Channel::Listener { + public: + typedef IDMap<ServiceProcessControl>::iterator iterator; + typedef std::queue<IPC::Message> MessageQueue; + + // An interface for handling messages received from the service process. + class MessageHandler { + public: + virtual ~MessageHandler() {} + // This is a test signal sent from the service process. This can be used + // the healthiness of the service. + virtual void OnGoodDay() = 0; + }; + + // Construct a ServiceProcessControl with |profile| and a specific |type|. + ServiceProcessControl(Profile* profile, ServiceProcessType type); + virtual ~ServiceProcessControl(); + + // Return the user profile associated with this service process. + Profile* profile() const { return profile_; } + + // Return the type of this object. + ServiceProcessType type() const { return type_; } + + // Return true if this object is connected to the service. + bool is_connected() const { return channel_.get() != NULL; } + + // Initialize the connection to the service process. + // |connect_done_task| is invoked if the connection has succeeded or failed. + // User should call is_connected() to check the connection status. + void Connect(Task* connect_done_task); + + // Create a new service process and connects to it. + // |launch_done_task| is called if launching the service process has failed + // or we have successfully launched the process and connected to it. + void Launch(Task* launch_done_task); + + // IPC::Channel::Listener implementation. + virtual void OnMessageReceived(const IPC::Message& message); + virtual void OnChannelConnected(int32 peer_pid); + virtual void OnChannelError(); + + // IPC::Channel::Sender implementation + virtual bool Send(IPC::Message* message); + + // Send a hello message to the service process for testing purpose. + // Return true if the message was sent. + bool SendHello(); + + // Send a shutdown message to the service process. IPC channel will be + // destroyed after calling this method. + // Return true if the message was sent. + bool Shutdown(); + + // Set the message handler for receiving messages from the service process. + // TODO(hclam): Allow more than 1 handler. + void SetMessageHandler(MessageHandler* message_handler) { + message_handler_ = message_handler; + } + + private: + class Launcher; + + // Method called by Launcher when the service process is launched. + void OnProcessLaunched(Task* launch_done_task); + + // Used internally to connect to the service process. + void ConnectInternal(); + + Profile* profile_; + ServiceProcessType type_; + + // IPC channel to the service process. + scoped_ptr<IPC::SyncChannel> channel_; + + // Service process launcher. + scoped_refptr<Launcher> launcher_; + + // Callback that gets invoked when the channel is connected or failed to + // connect. + scoped_ptr<Task> connect_done_task_; + + // Handler for messages from service process. + MessageHandler* message_handler_; +}; + +#endif // CHROME_BROWSER_SERVICE_SERVICE_PROCESS_H_ diff --git a/chrome/browser/service/service_process_control_browsertest.cc b/chrome/browser/service/service_process_control_browsertest.cc new file mode 100644 index 0000000..0dda5fa --- /dev/null +++ b/chrome/browser/service/service_process_control_browsertest.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2010 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 "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/service/service_process_control.h" +#include "chrome/browser/service/service_process_control_manager.h" +#include "chrome/common/service_process_type.h" + +class ServiceProcessControlBrowserTest + : public InProcessBrowserTest, + public ServiceProcessControl::MessageHandler { + protected: + void LaunchServiceProcessControl() { + ServiceProcessControl* process = + ServiceProcessControlManager::instance()->GetProcessControl( + browser()->profile(), kServiceProcessCloudPrint); + process_ = process; + + // Launch the process asynchronously. + process->Launch( + NewRunnableMethod( + this, + &ServiceProcessControlBrowserTest::ProcessControlLaunched)); + + // Then run the message loop to keep things running. + ui_test_utils::RunMessageLoop(); + } + + void SayHelloAndWait() { + // Send a hello message to the service process and wait for a reply. + process()->SendHello(); + ui_test_utils::RunMessageLoop(); + } + + void ProcessControlLaunched() { + process()->SetMessageHandler(this); + // Quit the current message. + MessageLoop::current()->Quit(); + } + + // ServiceProcessControl::MessageHandler implementations. + virtual void OnGoodDay() { + MessageLoop::current()->Quit(); + } + + ServiceProcessControl* process() { return process_; } + + private: + ServiceProcessControl* process_; +}; + +#if defined(OS_WIN) +// They way that the IPC is implemented only works on windows. This has to +// change when we implement a different scheme for IPC. +IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, LaunchAndIPC) { + LaunchServiceProcessControl(); + + // Make sure we are connected to the service process. + EXPECT_TRUE(process()->is_connected()); + SayHelloAndWait(); + + // And then shutdown the service process. + EXPECT_TRUE(process()->Shutdown()); +} +#endif + +DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcessControlBrowserTest); diff --git a/chrome/browser/service/service_process_control_manager.cc b/chrome/browser/service/service_process_control_manager.cc new file mode 100644 index 0000000..821de9a --- /dev/null +++ b/chrome/browser/service/service_process_control_manager.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2010 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 "chrome/browser/service/service_process_control_manager.h" + +#include "base/singleton.h" +#include "base/stl_util-inl.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/service/service_process_control.h" + +ServiceProcessControlManager::ServiceProcessControlManager() { +} + +ServiceProcessControlManager::~ServiceProcessControlManager() { + DCHECK(process_control_list_.empty()); +} + +ServiceProcessControl* ServiceProcessControlManager::GetProcessControl( + Profile* profile, ServiceProcessType type) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + // TODO(hclam): We will have different service process for different types of + // service, but now we only create a new process for a different profile. + for (ServiceProcessControlList::iterator i = process_control_list_.begin(); + i != process_control_list_.end(); ++i) { + if ((*i)->profile() == profile) + return *i; + } + + // Couldn't find a ServiceProcess so construct a new one. + ServiceProcessControl* process = new ServiceProcessControl(profile, type); + process_control_list_.push_back(process); + return process; +} + +void ServiceProcessControlManager::Shutdown() { + STLDeleteElements(&process_control_list_); +} + +// static +ServiceProcessControlManager* ServiceProcessControlManager::instance() { + return Singleton<ServiceProcessControlManager>::get(); +} diff --git a/chrome/browser/service/service_process_control_manager.h b/chrome/browser/service/service_process_control_manager.h new file mode 100644 index 0000000..c57f65c --- /dev/null +++ b/chrome/browser/service/service_process_control_manager.h @@ -0,0 +1,45 @@ +// Copyright (c) 2010 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 CHROME_BROWSER_SERVICE_SERVICE_PROCESS_CONTROL_MANAGER_H_ +#define CHROME_BROWSER_SERVICE_SERVICE_PROCESS_CONTROL_MANAGER_H_ + +#include <vector> + +#include "chrome/common/service_process_type.h" + +class Profile; +class ServiceProcessControl; + +// ServiceProcessControlManager is a registrar for all ServiceProcess created +// in the browser process. It is also a factory for creating new +// ServiceProcess. +class ServiceProcessControlManager { + public: + typedef std::vector<ServiceProcessControl*> ServiceProcessControlList; + + ServiceProcessControlManager(); + ~ServiceProcessControlManager(); + + // Get the ServiceProcess instance corresponding to |profile| and |type|. + // If such an instance doesn't exist a new instance is created. + // + // There will be at most one ServiceProcess for a |profile| and |type| + // pair. + // + // This method should only be accessed on the UI thread. + ServiceProcessControl* GetProcessControl(Profile* profile, + ServiceProcessType type); + + // Destroy all ServiceProcess objects created. + void Shutdown(); + + // Return the instance of ServiceProcessControlManager. + static ServiceProcessControlManager* instance(); + + private: + ServiceProcessControlList process_control_list_; +}; + +#endif // CHROME_BROWSER_SERVICE_SERVICE_PROCESS_CONTROL_MANAGER_H_ |