summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser.vcproj8
-rw-r--r--chrome/browser/extensions/extensions_service.cc230
-rw-r--r--chrome/browser/extensions/extensions_service.h40
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc14
-rw-r--r--chrome/browser/sandbox_policy.cc23
-rw-r--r--chrome/browser/sandbox_policy.h6
-rw-r--r--chrome/browser/utility_process_host.cc87
-rw-r--r--chrome/browser/utility_process_host.h79
8 files changed, 389 insertions, 98 deletions
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj
index 07b0fb27c..bb869e6 100644
--- a/chrome/browser/browser.vcproj
+++ b/chrome/browser/browser.vcproj
@@ -2734,6 +2734,14 @@
RelativePath=".\toolbar_model.h"
>
</File>
+ <File
+ RelativePath=".\utility_process_host.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\utility_process_host.h"
+ >
+ </File>
</Files>
<Globals>
</Globals>
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 05815a3..23c37fe 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -16,6 +16,7 @@
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/extensions/extension.h"
#include "chrome/browser/extensions/extension_browser_event_router.h"
#include "chrome/browser/extensions/extension_error_reporter.h"
@@ -23,6 +24,8 @@
#include "chrome/browser/extensions/user_script_master.h"
#include "chrome/browser/plugin_service.h"
#include "chrome/browser/profile.h"
+#include "chrome/browser/utility_process_host.h"
+#include "chrome/common/extensions/extension_unpacker.h"
#include "chrome/common/json_value_serializer.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/unzip.h"
@@ -69,15 +72,117 @@ const wchar_t kRegistryExtensionVersion[] = L"version";
// source.
const char kExternalInstallFile[] = "EXTERNAL_INSTALL";
+// A temporary subdirectory where we unpack extensions.
+const char* kUnpackExtensionDir = "TEMP_UNPACK";
+
// The version of the extension package that this code understands.
const uint32 kExpectedVersion = 1;
}
+// This class coordinates an extension unpack task which is run in a separate
+// process. Results are sent back to this class, which we route to the
+// ExtensionServiceBackend.
+class ExtensionsServiceBackend::UnpackerClient
+ : public UtilityProcessHost::Client {
+ public:
+ UnpackerClient(ExtensionsServiceBackend* backend,
+ const FilePath& extension_path,
+ const std::string& expected_id,
+ bool from_external)
+ : backend_(backend), extension_path_(extension_path),
+ expected_id_(expected_id), from_external_(from_external) {
+ }
+
+ // Starts the unpack task. We call back to the backend when the task is done,
+ // or a problem occurs.
+ void Start() {
+ AddRef(); // balanced in OnUnpackExtensionReply()
+
+ // TODO(mpcomplete): handle multiple installs
+ FilePath temp_dir = backend_->install_directory_.AppendASCII(
+ kUnpackExtensionDir);
+ if (!file_util::CreateDirectory(temp_dir)) {
+ backend_->ReportExtensionInstallError(extension_path_,
+ "Failed to create temporary directory.");
+ return;
+ }
+
+ temp_extension_path_ = temp_dir.Append(extension_path_.BaseName());
+ if (!file_util::CopyFile(extension_path_, temp_extension_path_)) {
+ backend_->ReportExtensionInstallError(extension_path_,
+ "Failed to copy extension file to temporary directory.");
+ return;
+ }
+
+ if (backend_->resource_dispatcher_host_) {
+ ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread,
+ backend_->resource_dispatcher_host_,
+ MessageLoop::current()));
+ } else {
+ // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're
+ // in a unit test and run the unpacker directly in-process.
+ ExtensionUnpacker unpacker(temp_extension_path_);
+ bool success = unpacker.Run();
+ OnUnpackExtensionReply(success, unpacker.error_message());
+ }
+ }
+
+ private:
+ // UtilityProcessHost::Client
+ virtual void OnProcessCrashed() {
+ OnUnpackExtensionReply(false, "Chrome crashed while trying to install");
+ }
+
+ virtual void OnUnpackExtensionReply(bool success,
+ const std::string& error_message) {
+ if (success) {
+ // The extension was unpacked to the temp dir inside our unpacking dir.
+ FilePath extension_dir = temp_extension_path_.DirName().AppendASCII(
+ ExtensionsServiceBackend::kTempExtensionName);
+ backend_->OnExtensionUnpacked(extension_path_, extension_dir,
+ expected_id_, from_external_);
+ } else {
+ backend_->ReportExtensionInstallError(extension_path_, error_message);
+ }
+ Cleanup();
+ Release(); // balanced in Run()
+ }
+
+ // Cleans up our temp directory.
+ void Cleanup() {
+ file_util::Delete(temp_extension_path_.DirName(), true);
+ }
+
+ // Starts the utility process that unpacks our extension.
+ void StartProcessOnIOThread(ResourceDispatcherHost* rdh,
+ MessageLoop* file_loop) {
+ UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop);
+ host->StartExtensionUnpacker(temp_extension_path_);
+ }
+
+ scoped_refptr<ExtensionsServiceBackend> backend_;
+
+ // The path to the crx file that we're installing.
+ FilePath extension_path_;
+
+ // The path to the copy of the crx file in the temporary directory where we're
+ // unpacking it.
+ FilePath temp_extension_path_;
+
+ // The ID we expect this extension to have, if any.
+ std::string expected_id_;
+
+ // True if this is being installed from an external source.
+ bool from_external_;
+};
+
ExtensionsService::ExtensionsService(Profile* profile,
UserScriptMaster* user_script_master)
: message_loop_(MessageLoop::current()),
install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)),
- backend_(new ExtensionsServiceBackend(install_directory_)),
+ backend_(new ExtensionsServiceBackend(
+ install_directory_, g_browser_process->resource_dispatcher_host())),
user_script_master_(user_script_master) {
}
@@ -677,100 +782,80 @@ void ExtensionsServiceBackend::InstallExtension(
frontend_ = frontend;
alert_on_error_ = false;
- bool was_update = false;
- FilePath destination_path;
- if (InstallOrUpdateExtension(extension_path,
- std::string(), // no expected id
- &destination_path, &was_update))
- ReportExtensionInstalled(destination_path.DirName(), was_update);
+ bool from_external = false;
+ InstallOrUpdateExtension(extension_path, std::string(), from_external);
}
-bool ExtensionsServiceBackend::InstallOrUpdateExtension(
- const FilePath& source_file, const std::string& expected_id,
- FilePath* version_dir, bool* was_update) {
- if (was_update)
- *was_update = false;
+void ExtensionsServiceBackend::InstallOrUpdateExtension(
+ const FilePath& extension_path, const std::string& expected_id,
+ bool from_external) {
+ UnpackerClient* client =
+ new UnpackerClient(this, extension_path, expected_id, from_external);
+ client->Start();
+}
- // Read and verify the extension.
- scoped_ptr<DictionaryValue> manifest(ReadManifest(source_file));
+void ExtensionsServiceBackend::OnExtensionUnpacked(
+ const FilePath& extension_path,
+ const FilePath& temp_extension_dir,
+ const std::string expected_id,
+ bool from_external) {
+ // TODO(mpcomplete): the utility process should pass up a parsed manifest that
+ // we rewrite in the browser.
+ // Bug http://code.google.com/p/chromium/issues/detail?id=11680
+ scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path));
if (!manifest.get()) {
// ReadManifest has already reported the extension error.
- return false;
+ return;
}
- DictionaryValue* dict = manifest.get();
+
Extension extension;
std::string error;
- if (!extension.InitFromValue(*dict,
+ if (!extension.InitFromValue(*manifest,
true, // require ID
&error)) {
- ReportExtensionInstallError(source_file,
- "Invalid extension manifest.");
- return false;
- }
-
- // ID is required for installed extensions.
- if (extension.id().empty()) {
- ReportExtensionInstallError(source_file, "Required value 'id' is missing.");
- return false;
+ ReportExtensionInstallError(extension_path, "Invalid extension manifest.");
+ return;
}
// If an expected id was provided, make sure it matches.
- if (expected_id.length() && expected_id != extension.id()) {
- ReportExtensionInstallError(source_file,
+ if (!expected_id.empty() && expected_id != extension.id()) {
+ ReportExtensionInstallError(extension_path,
"ID in new extension manifest does not match expected ID.");
- return false;
+ return;
}
// <profile>/Extensions/<id>
FilePath dest_dir = install_directory_.AppendASCII(extension.id());
std::string version = extension.VersionString();
std::string current_version;
+ bool was_update = false;
if (ReadCurrentVersion(dest_dir, &current_version)) {
if (!CheckCurrentVersion(version, current_version, dest_dir))
- return false;
- if (was_update)
- *was_update = true;
- }
-
- // <profile>/Extensions/INSTALL_TEMP
- FilePath temp_dir = install_directory_.AppendASCII(kTempExtensionName);
-
- // Ensure we're starting with a clean slate.
- if (file_util::PathExists(temp_dir)) {
- if (!file_util::Delete(temp_dir, true)) {
- ReportExtensionInstallError(source_file,
- "Couldn't delete existing temporary directory.");
- return false;
- }
- }
- ScopedTempDir scoped_temp;
- scoped_temp.Set(temp_dir);
- if (!scoped_temp.IsValid()) {
- ReportExtensionInstallError(source_file,
- "Couldn't create temporary directory.");
- return false;
- }
-
- // <profile>/Extensions/INSTALL_TEMP/<version>
- FilePath temp_version = temp_dir.AppendASCII(version);
- file_util::CreateDirectory(temp_version);
- if (!Unzip(source_file, temp_version, NULL)) {
- ReportExtensionInstallError(source_file, "Couldn't unzip extension.");
- return false;
+ return;
+ was_update = true;
}
// <profile>/Extensions/<dir_name>/<version>
- *version_dir = dest_dir.AppendASCII(version);
- if (!InstallDirSafely(temp_version, *version_dir))
- return false;
+ FilePath version_dir = dest_dir.AppendASCII(version);
+ if (!InstallDirSafely(temp_extension_dir, version_dir))
+ return;
if (!SetCurrentVersion(dest_dir, version)) {
- if (!file_util::Delete(*version_dir, true))
+ if (!file_util::Delete(version_dir, true))
LOG(WARNING) << "Can't remove " << dest_dir.value();
- return false;
+ return;
}
- return true;
+ // To mark that this extension was installed from an external source, create a
+ // zero-length file. At load time, this is used to indicate that the
+ // extension should be uninstalled.
+ // TODO(erikkay): move this into per-extension config storage when it appears.
+ if (from_external) {
+ FilePath marker = version_dir.AppendASCII(kExternalInstallFile);
+ file_util::WriteFile(marker, NULL, 0);
+ }
+
+ ReportExtensionInstalled(dest_dir, was_update);
}
void ExtensionsServiceBackend::ReportExtensionInstallError(
@@ -847,18 +932,9 @@ void ExtensionsServiceBackend::CheckForExternalUpdates(
std::wstring extension_version;
if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
if (ShouldInstall(id, WideToASCII(extension_version))) {
- FilePath version_dir;
- if (InstallOrUpdateExtension(FilePath(extension_path), id,
- &version_dir, NULL)) {
- // To mark that this extension was installed from an external
- // source, create a zero-length file. At load time, this is used
- // to indicate that the extension should be uninstalled.
- // TODO(erikkay): move this into per-extension config storage when
- // it appears.
- FilePath marker = version_dir.AppendASCII(
- kExternalInstallFile);
- file_util::WriteFile(marker, NULL, 0);
- }
+ bool from_external = true;
+ InstallOrUpdateExtension(FilePath(extension_path), id,
+ from_external);
}
} else {
// TODO(erikkay): find a way to get this into about:extensions
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index 481cdd2..b601b73 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -19,6 +19,7 @@ class Extension;
class ExtensionsServiceBackend;
class GURL;
class Profile;
+class ResourceDispatcherHost;
class SiteInstance;
class UserScriptMaster;
typedef std::vector<Extension*> ExtensionList;
@@ -118,8 +119,10 @@ class ExtensionsService : public ExtensionsServiceFrontendInterface {
class ExtensionsServiceBackend
: public base::RefCountedThreadSafe<ExtensionsServiceBackend> {
public:
- explicit ExtensionsServiceBackend(const FilePath& install_directory)
- : install_directory_(install_directory) {}
+ explicit ExtensionsServiceBackend(const FilePath& install_directory,
+ ResourceDispatcherHost* rdh)
+ : install_directory_(install_directory),
+ resource_dispatcher_host_(rdh) {}
// Loads extensions from the install directory. The extensions are assumed to
// be unpacked in directories that are direct children of the specified path.
@@ -157,6 +160,9 @@ class ExtensionsServiceBackend
void UninstallExtension(const std::string& extension_id);
private:
+ class UnpackerClient;
+ friend class UnpackerClient;
+
// Load a single extension from |extension_path|, the top directory of
// a specific extension where its manifest file lives.
Extension* LoadExtension(const FilePath& extension_path, bool require_id);
@@ -165,18 +171,23 @@ class ExtensionsServiceBackend
// a versioned extension where its Current Version file lives.
Extension* LoadExtensionCurrentVersion(const FilePath& extension_path);
- // Install a crx file at |source_file|. If |expected_id| is not empty, it's
- // verified against the extension's manifest before installation. If the
- // extension is already installed, install the new version only if its version
- // number is greater than the current installed version. On success, sets
- // |version_dir| to the versioned directory the extension was installed to and
- // |was_update| to whether the extension turned out to be an update to an
- // already installed version. Both |version_dir| and |was_update| can be NULL
- // if the caller doesn't care.
- bool InstallOrUpdateExtension(const FilePath& source_file,
+ // Install a crx file at |extension_path|. If |expected_id| is not empty, it's
+ // verified against the extension's manifest before installation. If
+ // |from_external| is true, this extension install is from an external source,
+ // ie the Windows registry, and will be marked as such. If the extension is
+ // already installed, install the new version only if its version number is
+ // greater than the current installed version.
+ void InstallOrUpdateExtension(const FilePath& extension_path,
const std::string& expected_id,
- FilePath* version_dir,
- bool* was_update);
+ bool from_external);
+
+ // Finish installing an extension after it has been unpacked to
+ // |temp_extension_dir| by our utility process. If |expected_id| is not
+ // empty, it's verified against the extension's manifest before installation.
+ void OnExtensionUnpacked(const FilePath& extension_path,
+ const FilePath& temp_extension_dir,
+ const std::string expected_id,
+ bool from_external);
// Notify the frontend that there was an error loading an extension.
void ReportExtensionLoadError(const FilePath& extension_path,
@@ -238,6 +249,9 @@ class ExtensionsServiceBackend
// The top-level extensions directory being installed to.
FilePath install_directory_;
+ // We only need a pointer to this to pass along to other interfaces.
+ ResourceDispatcherHost* resource_dispatcher_host_;
+
// Whether errors result in noisy alerts.
bool alert_on_error_;
diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc
index bc1a3e3..ea01c25 100644
--- a/chrome/browser/extensions/extensions_service_unittest.cc
+++ b/chrome/browser/extensions/extensions_service_unittest.cc
@@ -152,7 +152,7 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectorySuccess) {
extensions_path = extensions_path.AppendASCII("good");
scoped_refptr<ExtensionsServiceBackend> backend(
- new ExtensionsServiceBackend(extensions_path));
+ new ExtensionsServiceBackend(extensions_path, NULL));
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
@@ -234,7 +234,7 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectoryFail) {
extensions_path = extensions_path.AppendASCII("bad");
scoped_refptr<ExtensionsServiceBackend> backend(
- new ExtensionsServiceBackend(extensions_path));
+ new ExtensionsServiceBackend(extensions_path, NULL));
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
@@ -273,7 +273,7 @@ TEST_F(ExtensionsServiceTest, InstallExtension) {
file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("ext_test"),
&install_dir);
scoped_refptr<ExtensionsServiceBackend> backend(
- new ExtensionsServiceBackend(install_dir));
+ new ExtensionsServiceBackend(install_dir, NULL));
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
@@ -313,7 +313,7 @@ TEST_F(ExtensionsServiceTest, UninstallExtension) {
file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("ext_test"),
&install_path);
scoped_refptr<ExtensionsServiceBackend> backend(
- new ExtensionsServiceBackend(install_path));
+ new ExtensionsServiceBackend(install_path, NULL));
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
@@ -356,7 +356,7 @@ TEST_F(ExtensionsServiceTest, ReinstallExtension) {
file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("ext_test"),
&install_dir);
scoped_refptr<ExtensionsServiceBackend> backend(
- new ExtensionsServiceBackend(install_dir));
+ new ExtensionsServiceBackend(install_dir, NULL));
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
@@ -409,7 +409,7 @@ TEST_F(ExtensionsServiceTest, LoadExtension) {
extensions_path = extensions_path.AppendASCII("extensions");
scoped_refptr<ExtensionsServiceBackend> backend(
- new ExtensionsServiceBackend(extensions_path));
+ new ExtensionsServiceBackend(extensions_path, NULL));
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
@@ -437,7 +437,7 @@ TEST_F(ExtensionsServiceTest, GenerateID) {
extensions_path = extensions_path.AppendASCII("extensions");
scoped_refptr<ExtensionsServiceBackend> backend(
- new ExtensionsServiceBackend(extensions_path));
+ new ExtensionsServiceBackend(extensions_path, NULL));
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
diff --git a/chrome/browser/sandbox_policy.cc b/chrome/browser/sandbox_policy.cc
index 32c1f44..a50e01b 100644
--- a/chrome/browser/sandbox_policy.cc
+++ b/chrome/browser/sandbox_policy.cc
@@ -244,7 +244,6 @@ bool ApplyPolicyForUntrustedPlugin(sandbox::TargetPolicy* policy) {
sandbox::TargetPolicy::FILES_ALLOW_ANY, policy))
return false;
-
if (!AddDirectoryAndChildren(base::DIR_APP_DATA, NULL,
sandbox::TargetPolicy::FILES_ALLOW_READONLY,
policy))
@@ -343,6 +342,11 @@ void AddPolicyForRenderer(HDESK desktop, sandbox::TargetPolicy* policy) {
namespace sandbox {
base::ProcessHandle StartProcess(CommandLine* cmd_line) {
+ return StartProcessWithAccess(cmd_line, FilePath());
+}
+
+base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line,
+ const FilePath& exposed_dir) {
base::ProcessHandle process = 0;
const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
ChildProcessInfo::ProcessType type;
@@ -353,6 +357,8 @@ base::ProcessHandle StartProcess(CommandLine* cmd_line) {
type = ChildProcessInfo::PLUGIN_PROCESS;
} else if (type_str == switches::kWorkerProcess) {
type = ChildProcessInfo::WORKER_PROCESS;
+ } else if (type_str == switches::kUtilityProcess) {
+ type = ChildProcessInfo::UTILITY_PROCESS;
} else {
NOTREACHED();
return 0;
@@ -395,6 +401,21 @@ base::ProcessHandle StartProcess(CommandLine* cmd_line) {
AddPolicyForRenderer(desktop, policy);
}
+ if (!exposed_dir.empty()) {
+ result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ exposed_dir.ToWStringHack().c_str());
+ if (result != sandbox::SBOX_ALL_OK)
+ return 0;
+
+ FilePath exposed_files = exposed_dir.AppendASCII("*");
+ result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ exposed_files.ToWStringHack().c_str());
+ if (result != sandbox::SBOX_ALL_OK)
+ return 0;
+ }
+
if (!AddGenericPolicy(policy)) {
NOTREACHED();
if (desktop)
diff --git a/chrome/browser/sandbox_policy.h b/chrome/browser/sandbox_policy.h
index cc9b538..664f279 100644
--- a/chrome/browser/sandbox_policy.h
+++ b/chrome/browser/sandbox_policy.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_SANDBOX_POLICY_H_
#include "base/process.h"
+#include "base/file_path.h"
class CommandLine;
@@ -14,6 +15,11 @@ namespace sandbox {
// Starts a sandboxed process and returns a handle to it.
base::ProcessHandle StartProcess(CommandLine* cmd_line);
+// Starts a sandboxed process with the given directory unsandboxed
+// and returns a handle to it.
+base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line,
+ const FilePath& exposed_dir);
+
} // namespace sandbox;
#endif // CHROME_BROWSER_SANDBOX_POLICY_H_
diff --git a/chrome/browser/utility_process_host.cc b/chrome/browser/utility_process_host.cc
new file mode 100644
index 0000000..20b34d5
--- /dev/null
+++ b/chrome/browser/utility_process_host.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2009 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/utility_process_host.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/task.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/render_messages.h"
+
+#if defined(OS_WIN)
+#include "chrome/browser/sandbox_policy.h"
+#endif
+
+UtilityProcessHost::UtilityProcessHost(ResourceDispatcherHost* rdh,
+ Client* client,
+ MessageLoop* client_loop)
+ : ChildProcessHost(UTILITY_PROCESS, rdh),
+ client_(client),
+ client_loop_(client_loop) {
+}
+
+UtilityProcessHost::~UtilityProcessHost() {
+}
+
+bool UtilityProcessHost::StartExtensionUnpacker(const FilePath& extension) {
+ // Grant the subprocess access to the entire subdir the extension file is
+ // in, so that it can unpack to that dir.
+ if (!StartProcess(extension.DirName()))
+ return false;
+
+ Send(new UtilityMsg_UnpackExtension(extension));
+ return true;
+}
+
+bool UtilityProcessHost::StartProcess(const FilePath& exposed_dir) {
+ if (!CreateChannel())
+ return false;
+
+ std::wstring exe_path;
+ if (!PathService::Get(base::FILE_EXE, &exe_path))
+ return false;
+
+ CommandLine cmd_line(exe_path);
+ cmd_line.AppendSwitchWithValue(switches::kProcessType,
+ switches::kUtilityProcess);
+ cmd_line.AppendSwitchWithValue(switches::kProcessChannelID, channel_id());
+
+ base::ProcessHandle process;
+#if defined(OS_WIN)
+ process = sandbox::StartProcessWithAccess(&cmd_line, exposed_dir);
+#else
+ // TODO(port): sandbox
+ base::LaunchApp(cmd_line, false, false, &process);
+#endif
+ if (!process)
+ return false;
+ SetHandle(process);
+
+ return true;
+}
+
+void UtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
+ client_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(client_.get(), &Client::OnMessageReceived, message));
+}
+
+void UtilityProcessHost::OnChannelError() {
+ bool child_exited;
+ bool did_crash = base::DidProcessCrash(&child_exited, handle());
+ if (did_crash) {
+ client_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(client_.get(), &Client::OnProcessCrashed));
+ }
+}
+
+void UtilityProcessHost::Client::OnMessageReceived(
+ const IPC::Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(UtilityProcessHost, message)
+ IPC_MESSAGE_HANDLER(UtilityHostMsg_UnpackExtension_Reply,
+ Client::OnUnpackExtensionReply)
+ IPC_END_MESSAGE_MAP_EX()
+}
diff --git a/chrome/browser/utility_process_host.h b/chrome/browser/utility_process_host.h
new file mode 100644
index 0000000..8cb281e
--- /dev/null
+++ b/chrome/browser/utility_process_host.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2009 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_UTILITY_PROCESS_HOST_H_
+#define CHROME_BROWSER_UTILITY_PROCESS_HOST_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "chrome/common/child_process_host.h"
+#include "chrome/common/ipc_channel.h"
+
+class CommandLine;
+class MessageLoop;
+
+// This class acts as the browser-side host to a utility child process. A
+// utility process is a short-lived sandboxed process that is created to run
+// a specific task. This class lives solely on the IO thread.
+class UtilityProcessHost : public ChildProcessHost {
+ public:
+ // An interface to be implemented by consumers of the utility process to
+ // get results back. All functions are called on the thread passed along
+ // to UtilityProcessHost.
+ class Client : public base::RefCountedThreadSafe<Client> {
+ public:
+ Client() {}
+ virtual ~Client() {}
+
+ // Called when the process has crashed.
+ virtual void OnProcessCrashed() {}
+
+ // Called when the process sends a reply to an UnpackExtension message.
+ // If success if false, error_message contains a description of the problem.
+ virtual void OnUnpackExtensionReply(bool success,
+ const std::string& error_message) {}
+ private:
+ friend class UtilityProcessHost;
+ void OnMessageReceived(const IPC::Message& message);
+
+ DISALLOW_COPY_AND_ASSIGN(Client);
+ };
+
+ UtilityProcessHost(ResourceDispatcherHost* rdh, Client* client,
+ MessageLoop* client_loop);
+ ~UtilityProcessHost();
+
+ // Start a process to unpack the extension at the given path. The process
+ // will be given access to the directory subtree that the extension file is
+ // in, so the caller is expected to have moved that file into a quarantined
+ // location first.
+ bool StartExtensionUnpacker(const FilePath& extension);
+
+ private:
+ // Starts the process. Returns true iff it succeeded.
+ bool StartProcess(const FilePath& exposed_dir);
+
+ // IPC messages:
+ void OnMessageReceived(const IPC::Message& message);
+
+ // ChildProcessHost:
+ virtual void OnChannelError();
+ virtual bool CanShutdown() { return true; }
+ virtual URLRequestContext* GetRequestContext(
+ uint32 request_id,
+ const ViewHostMsg_Resource_Request& request_data) {
+ return NULL;
+ }
+
+ // A pointer to our client interface, who will be informed of progress.
+ scoped_refptr<Client> client_;
+ MessageLoop* client_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(UtilityProcessHost);
+};
+
+#endif // CHROME_BROWSER_UTILITY_PROCESS_HOST_H_