summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser.cc4
-rw-r--r--chrome/browser/service/service_process_control.cc212
-rw-r--r--chrome/browser/service/service_process_control.h116
-rw-r--r--chrome/browser/service/service_process_control_browsertest.cc70
-rw-r--r--chrome/browser/service/service_process_control_manager.cc45
-rw-r--r--chrome/browser/service/service_process_control_manager.h45
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_