summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-22 22:58:22 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-22 22:58:22 +0000
commitdf8e899b92196a772511a165130f1fe08e199cb8 (patch)
tree893ca8821adc6165823f3c9a10dd0edfeb2e49e1 /content
parent5b77de94051020ca0aef549dee0cb33f7a737d88 (diff)
downloadchromium_src-df8e899b92196a772511a165130f1fe08e199cb8.zip
chromium_src-df8e899b92196a772511a165130f1fe08e199cb8.tar.gz
chromium_src-df8e899b92196a772511a165130f1fe08e199cb8.tar.bz2
Move core pieces of chrome\browser. I've only gone up to "g", will do the rest in another cl.
TBR=avi Review URL: http://codereview.chromium.org/6538100 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75652 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/browser/browser_child_process_host.cc249
-rw-r--r--content/browser/browser_child_process_host.h143
-rw-r--r--content/browser/browser_message_filter.cc92
-rw-r--r--content/browser/browser_message_filter.h64
-rw-r--r--content/browser/browser_thread.cc233
-rw-r--r--content/browser/browser_thread.h209
-rw-r--r--content/browser/browser_thread_unittest.cc166
-rw-r--r--content/browser/browsing_instance.cc149
-rw-r--r--content/browser/browsing_instance.h135
-rw-r--r--content/browser/cancelable_request.cc106
-rw-r--r--content/browser/cancelable_request.h704
-rw-r--r--content/browser/cert_store.cc146
-rw-r--r--content/browser/cert_store.h84
-rw-r--r--content/browser/certificate_manager_model.cc116
-rw-r--r--content/browser/certificate_manager_model.h110
-rw-r--r--content/browser/certificate_viewer.cc18
-rw-r--r--content/browser/certificate_viewer.h25
-rw-r--r--content/browser/child_process_launcher.cc348
-rw-r--r--content/browser/child_process_launcher.h71
-rw-r--r--content/browser/child_process_security_policy.cc413
-rw-r--r--content/browser/child_process_security_policy.h164
-rw-r--r--content/browser/child_process_security_policy_browsertest.cc52
-rw-r--r--content/browser/child_process_security_policy_unittest.cc333
-rw-r--r--content/browser/chrome_blob_storage_context.cc22
-rw-r--r--content/browser/chrome_blob_storage_context.h46
-rw-r--r--content/browser/cross_site_request_manager.cc38
-rw-r--r--content/browser/cross_site_request_manager.h60
-rw-r--r--content/browser/disposition_utils.cc30
-rw-r--r--content/browser/disposition_utils.h24
-rw-r--r--content/content_browser.gypi33
30 files changed, 4383 insertions, 0 deletions
diff --git a/content/browser/browser_child_process_host.cc b/content/browser/browser_child_process_host.cc
new file mode 100644
index 0000000..23ed85d
--- /dev/null
+++ b/content/browser/browser_child_process_host.cc
@@ -0,0 +1,249 @@
+// 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 "content/browser/browser_child_process_host.h"
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "chrome/app/breakpad_mac.h"
+#include "chrome/common/child_process_logging.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths_internal.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/plugin_messages.h"
+#include "chrome/common/process_watcher.h"
+#include "chrome/common/result_codes.h"
+#include "chrome/installer/util/google_update_settings.h"
+#include "content/browser/browser_thread.h"
+
+#if defined(OS_LINUX)
+#include "base/linux_util.h"
+#endif // OS_LINUX
+
+namespace {
+
+typedef std::list<BrowserChildProcessHost*> ChildProcessList;
+static base::LazyInstance<ChildProcessList> g_child_process_list(
+ base::LINKER_INITIALIZED);
+
+// The NotificationTask is used to notify about plugin process connection/
+// disconnection. It is needed because the notifications in the
+// NotificationService must happen in the main thread.
+class ChildNotificationTask : public Task {
+ public:
+ ChildNotificationTask(
+ NotificationType notification_type, ChildProcessInfo* info)
+ : notification_type_(notification_type), info_(*info) { }
+
+ virtual void Run() {
+ NotificationService::current()->
+ Notify(notification_type_, NotificationService::AllSources(),
+ Details<ChildProcessInfo>(&info_));
+ }
+
+ private:
+ NotificationType notification_type_;
+ ChildProcessInfo info_;
+};
+
+} // namespace
+
+
+BrowserChildProcessHost::BrowserChildProcessHost(
+ ChildProcessInfo::ProcessType type,
+ ResourceDispatcherHost* resource_dispatcher_host,
+ ResourceMessageFilter::URLRequestContextOverride*
+ url_request_context_override)
+ : ChildProcessInfo(type, -1),
+ ALLOW_THIS_IN_INITIALIZER_LIST(client_(this)),
+ resource_dispatcher_host_(resource_dispatcher_host) {
+ Initialize(url_request_context_override);
+}
+
+BrowserChildProcessHost::BrowserChildProcessHost(
+ ChildProcessInfo::ProcessType type,
+ ResourceDispatcherHost* resource_dispatcher_host)
+ : ChildProcessInfo(type, -1),
+ ALLOW_THIS_IN_INITIALIZER_LIST(client_(this)),
+ resource_dispatcher_host_(resource_dispatcher_host) {
+ Initialize(NULL);
+}
+
+void BrowserChildProcessHost::Initialize(
+ ResourceMessageFilter::URLRequestContextOverride*
+ url_request_context_override) {
+ if (resource_dispatcher_host_) {
+ ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter(
+ id(), type(), resource_dispatcher_host_);
+ if (url_request_context_override) {
+ resource_message_filter->set_url_request_context_override(
+ url_request_context_override);
+ }
+ AddFilter(resource_message_filter);
+ }
+
+ g_child_process_list.Get().push_back(this);
+}
+
+BrowserChildProcessHost::~BrowserChildProcessHost() {
+ g_child_process_list.Get().remove(this);
+}
+
+// static
+void BrowserChildProcessHost::SetCrashReporterCommandLine(
+ CommandLine* command_line) {
+#if defined(USE_LINUX_BREAKPAD)
+ if (IsCrashReporterEnabled()) {
+ command_line->AppendSwitchASCII(switches::kEnableCrashReporter,
+ child_process_logging::GetClientId() + "," + base::GetLinuxDistro());
+ }
+#elif defined(OS_MACOSX)
+ if (IsCrashReporterEnabled()) {
+ command_line->AppendSwitchASCII(switches::kEnableCrashReporter,
+ child_process_logging::GetClientId());
+ }
+#endif // OS_MACOSX
+}
+
+// static
+void BrowserChildProcessHost::TerminateAll() {
+ // Make a copy since the ChildProcessHost dtor mutates the original list.
+ ChildProcessList copy = g_child_process_list.Get();
+ STLDeleteElements(&copy);
+}
+
+void BrowserChildProcessHost::Launch(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& environ,
+#endif
+ CommandLine* cmd_line) {
+ child_process_.reset(new ChildProcessLauncher(
+#if defined(OS_WIN)
+ exposed_dir,
+#elif defined(OS_POSIX)
+ use_zygote,
+ environ,
+ channel()->GetClientFileDescriptor(),
+#endif
+ cmd_line,
+ &client_));
+}
+
+base::ProcessHandle BrowserChildProcessHost::GetChildProcessHandle() const {
+ DCHECK(child_process_.get())
+ << "Requesting a child process handle before launching.";
+ DCHECK(child_process_->GetHandle())
+ << "Requesting a child process handle before launch has completed OK.";
+ return child_process_->GetHandle();
+}
+
+void BrowserChildProcessHost::ForceShutdown() {
+ g_child_process_list.Get().remove(this);
+ ChildProcessHost::ForceShutdown();
+}
+
+void BrowserChildProcessHost::Notify(NotificationType type) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, new ChildNotificationTask(type, this));
+}
+
+base::TerminationStatus BrowserChildProcessHost::GetChildTerminationStatus(
+ int* exit_code) {
+ return child_process_->GetChildTerminationStatus(exit_code);
+}
+
+void BrowserChildProcessHost::OnChildDied() {
+ if (handle() != base::kNullProcessHandle) {
+ int exit_code;
+ base::TerminationStatus status = GetChildTerminationStatus(&exit_code);
+ switch (status) {
+ case base::TERMINATION_STATUS_PROCESS_CRASHED: {
+ OnProcessCrashed(exit_code);
+
+ // Report that this child process crashed.
+ Notify(NotificationType::CHILD_PROCESS_CRASHED);
+ UMA_HISTOGRAM_COUNTS("ChildProcess.Crashes", this->type());
+ break;
+ }
+ case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: {
+ OnProcessWasKilled(exit_code);
+
+ // Report that this child process was killed.
+ Notify(NotificationType::CHILD_PROCESS_WAS_KILLED);
+ UMA_HISTOGRAM_COUNTS("ChildProcess.Kills", this->type());
+ break;
+ }
+ default:
+ break;
+ }
+ // Notify in the main loop of the disconnection.
+ Notify(NotificationType::CHILD_PROCESS_HOST_DISCONNECTED);
+ }
+ ChildProcessHost::OnChildDied();
+}
+
+void BrowserChildProcessHost::ShutdownStarted() {
+ // Must remove the process from the list now, in case it gets used for a
+ // new instance before our watcher tells us that the process terminated.
+ g_child_process_list.Get().remove(this);
+}
+
+BrowserChildProcessHost::ClientHook::ClientHook(BrowserChildProcessHost* host)
+ : host_(host) {
+}
+
+void BrowserChildProcessHost::ClientHook::OnProcessLaunched() {
+ if (!host_->child_process_->GetHandle()) {
+ host_->OnChildDied();
+ return;
+ }
+ host_->set_handle(host_->child_process_->GetHandle());
+ host_->OnProcessLaunched();
+}
+
+BrowserChildProcessHost::Iterator::Iterator()
+ : all_(true), type_(UNKNOWN_PROCESS) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) <<
+ "ChildProcessInfo::Iterator must be used on the IO thread.";
+ iterator_ = g_child_process_list.Get().begin();
+}
+
+BrowserChildProcessHost::Iterator::Iterator(ChildProcessInfo::ProcessType type)
+ : all_(false), type_(type) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) <<
+ "ChildProcessInfo::Iterator must be used on the IO thread.";
+ iterator_ = g_child_process_list.Get().begin();
+ if (!Done() && (*iterator_)->type() != type_)
+ ++(*this);
+}
+
+BrowserChildProcessHost* BrowserChildProcessHost::Iterator::operator++() {
+ do {
+ ++iterator_;
+ if (Done())
+ break;
+
+ if (!all_ && (*iterator_)->type() != type_)
+ continue;
+
+ return *iterator_;
+ } while (true);
+
+ return NULL;
+}
+
+bool BrowserChildProcessHost::Iterator::Done() {
+ return iterator_ == g_child_process_list.Get().end();
+}
diff --git a/content/browser/browser_child_process_host.h b/content/browser/browser_child_process_host.h
new file mode 100644
index 0000000..f57ec5f
--- /dev/null
+++ b/content/browser/browser_child_process_host.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2011 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_CHILD_PROCESS_HOST_H_
+#define CONTENT_BROWSER_BROWSER_CHILD_PROCESS_HOST_H_
+#pragma once
+
+#include <list>
+
+#include "chrome/common/child_process_host.h"
+#include "chrome/common/child_process_info.h"
+#include "content/browser/child_process_launcher.h"
+#include "content/browser/renderer_host/resource_message_filter.h"
+
+class ResourceDispatcherHost;
+
+// Plugins/workers and other child processes that live on the IO thread should
+// derive from this class.
+//
+// [Browser]RenderProcessHost is the main exception that doesn't derive from
+// this class. That project lives on the UI thread.
+class BrowserChildProcessHost : public ChildProcessHost,
+ public ChildProcessInfo,
+ public ChildProcessLauncher::Client {
+ public:
+ virtual ~BrowserChildProcessHost();
+
+ // Prepares command_line for crash reporting as appropriate. On Linux and
+ // Mac, a command-line flag to enable crash reporting in the child process
+ // will be appended if needed, because the child process may not have access
+ // to the data that determines the status of crash reporting in the
+ // currently-executing process. This function is a no-op on Windows.
+ static void SetCrashReporterCommandLine(CommandLine* command_line);
+
+ // Terminates all child processes and deletes each ChildProcessHost instance.
+ static void TerminateAll();
+
+ // The Iterator class allows iteration through either all child processes, or
+ // ones of a specific type, depending on which constructor is used. Note that
+ // this should be done from the IO thread and that the iterator should not be
+ // kept around as it may be invalidated on subsequent event processing in the
+ // event loop.
+ class Iterator {
+ public:
+ Iterator();
+ explicit Iterator(ChildProcessInfo::ProcessType type);
+ BrowserChildProcessHost* operator->() { return *iterator_; }
+ BrowserChildProcessHost* operator*() { return *iterator_; }
+ BrowserChildProcessHost* operator++();
+ bool Done();
+
+ private:
+ bool all_;
+ ChildProcessInfo::ProcessType type_;
+ std::list<BrowserChildProcessHost*>::iterator iterator_;
+ };
+
+ protected:
+ // |resource_dispatcher_host| may be NULL to indicate none is needed for
+ // this process type.
+ // |url_request_context_getter| allows derived classes to override the
+ // net::URLRequestContext.
+ BrowserChildProcessHost(
+ ChildProcessInfo::ProcessType type,
+ ResourceDispatcherHost* resource_dispatcher_host,
+ ResourceMessageFilter::URLRequestContextOverride*
+ url_request_context_override);
+
+ // A convenient constructor for those classes that want to use the default
+ // net::URLRequestContext.
+ BrowserChildProcessHost(
+ ChildProcessInfo::ProcessType type,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // Derived classes call this to launch the child process asynchronously.
+ void Launch(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& environ,
+#endif
+ CommandLine* cmd_line);
+
+ // Returns the handle of the child process. This can be called only after
+ // OnProcessLaunched is called or it will be invalid and may crash.
+ base::ProcessHandle GetChildProcessHandle() const;
+
+ // ChildProcessLauncher::Client implementation.
+ virtual void OnProcessLaunched() {}
+
+ // Derived classes can override this to know if the process crashed.
+ // |exit_code| is the status returned when the process crashed (for
+ // posix, as returned from waitpid(), for Windows, as returned from
+ // GetExitCodeProcess()).
+ virtual void OnProcessCrashed(int exit_code) {}
+
+ // Derived classes can override this to know if the process was
+ // killed. |exit_code| is the status returned when the process
+ // was killed (for posix, as returned from waitpid(), for Windows,
+ // as returned from GetExitCodeProcess()).
+ virtual void OnProcessWasKilled(int exit_code) {}
+
+ // Returns the termination status of a child. |exit_code| is the
+ // status returned when the process exited (for posix, as returned
+ // from waitpid(), for Windows, as returned from
+ // GetExitCodeProcess()). |exit_code| may be NULL.
+ virtual base::TerminationStatus GetChildTerminationStatus(int* exit_code);
+
+ // Overrides from ChildProcessHost
+ virtual void OnChildDied();
+ virtual void ShutdownStarted();
+ virtual void Notify(NotificationType type);
+ // Extends the base class implementation and removes this host from
+ // the host list. Calls ChildProcessHost::ForceShutdown
+ virtual void ForceShutdown();
+
+ ResourceDispatcherHost* resource_dispatcher_host() {
+ return resource_dispatcher_host_;
+ }
+
+ private:
+ void Initialize(ResourceMessageFilter::URLRequestContextOverride*
+ url_request_context_override);
+
+ // By using an internal class as the ChildProcessLauncher::Client, we can
+ // intercept OnProcessLaunched and do our own processing before
+ // calling the subclass' implementation.
+ class ClientHook : public ChildProcessLauncher::Client {
+ public:
+ explicit ClientHook(BrowserChildProcessHost* host);
+ virtual void OnProcessLaunched();
+ private:
+ BrowserChildProcessHost* host_;
+ };
+ ClientHook client_;
+ // May be NULL if this current process has no resource dispatcher host.
+ ResourceDispatcherHost* resource_dispatcher_host_;
+ scoped_ptr<ChildProcessLauncher> child_process_;
+};
+
+#endif // CONTENT_BROWSER_BROWSER_CHILD_PROCESS_HOST_H_
diff --git a/content/browser/browser_message_filter.cc b/content/browser/browser_message_filter.cc
new file mode 100644
index 0000000..e116153
--- /dev/null
+++ b/content/browser/browser_message_filter.cc
@@ -0,0 +1,92 @@
+// 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 "content/browser/browser_message_filter.h"
+
+#include "base/logging.h"
+#include "base/process.h"
+#include "base/process_util.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/common/result_codes.h"
+
+BrowserMessageFilter::BrowserMessageFilter()
+ : channel_(NULL), peer_handle_(base::kNullProcessHandle) {
+}
+
+BrowserMessageFilter::~BrowserMessageFilter() {
+ base::CloseProcessHandle(peer_handle_);
+}
+
+void BrowserMessageFilter::OnFilterAdded(IPC::Channel* channel) {
+ channel_ = channel;
+}
+
+void BrowserMessageFilter::OnChannelClosing() {
+ channel_ = NULL;
+}
+
+void BrowserMessageFilter::OnChannelConnected(int32 peer_pid) {
+ if (!base::OpenProcessHandle(peer_pid, &peer_handle_)) {
+ NOTREACHED();
+ }
+}
+
+bool BrowserMessageFilter::Send(IPC::Message* message) {
+ if (message->is_sync()) {
+ // We don't support sending synchronous messages from the browser. If we
+ // really needed it, we can make this class derive from SyncMessageFilter
+ // but it seems better to not allow sending synchronous messages from the
+ // browser, since it might allow a corrupt/malicious renderer to hang us.
+ NOTREACHED() << "Can't send sync message through BrowserMessageFilter!";
+ return false;
+ }
+
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(this, &BrowserMessageFilter::Send, message));
+ return true;
+ }
+
+ if (channel_)
+ return channel_->Send(message);
+
+ delete message;
+ return false;
+}
+
+void BrowserMessageFilter::OverrideThreadForMessage(const IPC::Message& message,
+ BrowserThread::ID* thread) {
+}
+
+bool BrowserMessageFilter::OnMessageReceived(const IPC::Message& message) {
+ BrowserThread::ID thread = BrowserThread::IO;
+ OverrideThreadForMessage(message, &thread);
+ if (thread == BrowserThread::IO)
+ return DispatchMessage(message);
+
+ BrowserThread::PostTask(
+ thread, FROM_HERE,
+ NewRunnableMethod(
+ this, &BrowserMessageFilter::DispatchMessage, message));
+ return true;
+}
+
+bool BrowserMessageFilter::DispatchMessage(const IPC::Message& message) {
+ bool message_was_ok = true;
+ bool rv = OnMessageReceived(message, &message_was_ok);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) || rv) <<
+ "Must handle messages that were dispatched to another thread!";
+ if (!message_was_ok) {
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_BMF"));
+ BadMessageReceived();
+ }
+
+ return rv;
+}
+
+void BrowserMessageFilter::BadMessageReceived() {
+ base::KillProcess(peer_handle(), ResultCodes::KILLED_BAD_MESSAGE, false);
+}
diff --git a/content/browser/browser_message_filter.h b/content/browser/browser_message_filter.h
new file mode 100644
index 0000000..e557dfd
--- /dev/null
+++ b/content/browser/browser_message_filter.h
@@ -0,0 +1,64 @@
+// 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 CONTENT_BROWSER_BROWSER_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_BROWSER_MESSAGE_FILTER_H_
+#pragma once
+
+#include "base/process.h"
+#include "content/browser/browser_thread.h"
+#include "ipc/ipc_channel_proxy.h"
+
+// Base class for message filters in the browser process. You can receive and
+// send messages on any thread.
+class BrowserMessageFilter : public IPC::ChannelProxy::MessageFilter,
+ public IPC::Message::Sender {
+ public:
+ BrowserMessageFilter();
+ virtual ~BrowserMessageFilter();
+
+ // IPC::ChannelProxy::MessageFilter methods. If you override them, make sure
+ // to call them as well. These are always called on the IO thread.
+ virtual void OnFilterAdded(IPC::Channel* channel);
+ virtual void OnChannelClosing();
+ virtual void OnChannelConnected(int32 peer_pid);
+ // DON'T OVERRIDE THIS! Override the other version below.
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ // IPC::Message::Sender implementation. Can be called on any thread. Can't
+ // send sync messages (since we don't want to block the browser on any other
+ // process).
+ virtual bool Send(IPC::Message* message);
+
+ // If you want the given message to be dispatched to your OnMessageReceived on
+ // a different thread, change |thread| to the id of the target thread.
+ // If you don't handle this message, or want to keep it on the IO thread, do
+ // nothing.
+ virtual void OverrideThreadForMessage(const IPC::Message& message,
+ BrowserThread::ID* thread);
+
+ // Override this to receive messages.
+ // Your function will normally be called on the IO thread. However, if your
+ // OverrideThreadForMessage modifies the thread used to dispatch the message,
+ // your function will be called on the requested thread.
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) = 0;
+
+ // Can be called on any thread, after OnChannelConnected is called.
+ base::ProcessHandle peer_handle() { return peer_handle_; }
+
+ protected:
+ // Call this if a message couldn't be deserialized. This kills the renderer.
+ // Can be called on any thread.
+ virtual void BadMessageReceived();
+
+ private:
+ // Dispatches a message to the derived class.
+ bool DispatchMessage(const IPC::Message& message);
+
+ IPC::Channel* channel_;
+ base::ProcessHandle peer_handle_;
+};
+
+#endif // CONTENT_BROWSER_BROWSER_MESSAGE_FILTER_H_
diff --git a/content/browser/browser_thread.cc b/content/browser/browser_thread.cc
new file mode 100644
index 0000000..c0e4355
--- /dev/null
+++ b/content/browser/browser_thread.cc
@@ -0,0 +1,233 @@
+// 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 "content/browser/browser_thread.h"
+
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/threading/thread_restrictions.h"
+
+// Friendly names for the well-known threads.
+static const char* browser_thread_names[BrowserThread::ID_COUNT] = {
+ "", // UI (name assembled in browser_main.cc).
+ "Chrome_DBThread", // DB
+ "Chrome_WebKitThread", // WEBKIT
+ "Chrome_FileThread", // FILE
+ "Chrome_ProcessLauncherThread", // PROCESS_LAUNCHER
+ "Chrome_CacheThread", // CACHE
+ "Chrome_IOThread", // IO
+#if defined(USE_X11)
+ "Chrome_Background_X11Thread", // BACKGROUND_X11
+#endif
+};
+
+// An implementation of MessageLoopProxy to be used in conjunction
+// with BrowserThread.
+class BrowserThreadMessageLoopProxy : public base::MessageLoopProxy {
+ public:
+ explicit BrowserThreadMessageLoopProxy(BrowserThread::ID identifier)
+ : id_(identifier) {
+ }
+
+ // MessageLoopProxy implementation.
+ virtual bool PostTask(const tracked_objects::Location& from_here,
+ Task* task) {
+ return BrowserThread::PostTask(id_, from_here, task);
+ }
+
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ Task* task, int64 delay_ms) {
+ return BrowserThread::PostDelayedTask(id_, from_here, task, delay_ms);
+ }
+
+ virtual bool PostNonNestableTask(const tracked_objects::Location& from_here,
+ Task* task) {
+ return BrowserThread::PostNonNestableTask(id_, from_here, task);
+ }
+
+ virtual bool PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ Task* task,
+ int64 delay_ms) {
+ return BrowserThread::PostNonNestableDelayedTask(id_, from_here, task,
+ delay_ms);
+ }
+ virtual bool BelongsToCurrentThread() {
+ return BrowserThread::CurrentlyOn(id_);
+ }
+
+ private:
+ BrowserThread::ID id_;
+ DISALLOW_COPY_AND_ASSIGN(BrowserThreadMessageLoopProxy);
+};
+
+
+base::Lock BrowserThread::lock_;
+
+BrowserThread* BrowserThread::browser_threads_[ID_COUNT];
+
+BrowserThread::BrowserThread(BrowserThread::ID identifier)
+ : Thread(browser_thread_names[identifier]),
+ identifier_(identifier) {
+ Initialize();
+}
+
+BrowserThread::BrowserThread(ID identifier, MessageLoop* message_loop)
+ : Thread(message_loop->thread_name().c_str()),
+ identifier_(identifier) {
+ set_message_loop(message_loop);
+ Initialize();
+}
+
+void BrowserThread::Initialize() {
+ base::AutoLock lock(lock_);
+ DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT);
+ DCHECK(browser_threads_[identifier_] == NULL);
+ browser_threads_[identifier_] = this;
+}
+
+BrowserThread::~BrowserThread() {
+ // Stop the thread here, instead of the parent's class destructor. This is so
+ // that if there are pending tasks that run, code that checks that it's on the
+ // correct BrowserThread succeeds.
+ Stop();
+
+ base::AutoLock lock(lock_);
+ browser_threads_[identifier_] = NULL;
+#ifndef NDEBUG
+ // Double check that the threads are ordered correctly in the enumeration.
+ for (int i = identifier_ + 1; i < ID_COUNT; ++i) {
+ DCHECK(!browser_threads_[i]) <<
+ "Threads must be listed in the reverse order that they die";
+ }
+#endif
+}
+
+// static
+bool BrowserThread::IsWellKnownThread(ID identifier) {
+ base::AutoLock lock(lock_);
+ return (identifier >= 0 && identifier < ID_COUNT &&
+ browser_threads_[identifier]);
+}
+
+// static
+bool BrowserThread::CurrentlyOn(ID identifier) {
+ // We shouldn't use MessageLoop::current() since it uses LazyInstance which
+ // may be deleted by ~AtExitManager when a WorkerPool thread calls this
+ // function.
+ // http://crbug.com/63678
+ base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
+ base::AutoLock lock(lock_);
+ DCHECK(identifier >= 0 && identifier < ID_COUNT);
+ return browser_threads_[identifier] &&
+ browser_threads_[identifier]->message_loop() == MessageLoop::current();
+}
+
+// static
+bool BrowserThread::IsMessageLoopValid(ID identifier) {
+ base::AutoLock lock(lock_);
+ DCHECK(identifier >= 0 && identifier < ID_COUNT);
+ return browser_threads_[identifier] &&
+ browser_threads_[identifier]->message_loop();
+}
+
+// static
+bool BrowserThread::PostTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task) {
+ return PostTaskHelper(identifier, from_here, task, 0, true);
+}
+
+// static
+bool BrowserThread::PostDelayedTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task,
+ int64 delay_ms) {
+ return PostTaskHelper(identifier, from_here, task, delay_ms, true);
+}
+
+// static
+bool BrowserThread::PostNonNestableTask(
+ ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task) {
+ return PostTaskHelper(identifier, from_here, task, 0, false);
+}
+
+// static
+bool BrowserThread::PostNonNestableDelayedTask(
+ ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task,
+ int64 delay_ms) {
+ return PostTaskHelper(identifier, from_here, task, delay_ms, false);
+}
+
+// static
+bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) {
+ // We shouldn't use MessageLoop::current() since it uses LazyInstance which
+ // may be deleted by ~AtExitManager when a WorkerPool thread calls this
+ // function.
+ // http://crbug.com/63678
+ base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
+ MessageLoop* cur_message_loop = MessageLoop::current();
+ for (int i = 0; i < ID_COUNT; ++i) {
+ if (browser_threads_[i] &&
+ browser_threads_[i]->message_loop() == cur_message_loop) {
+ *identifier = browser_threads_[i]->identifier_;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// static
+scoped_refptr<base::MessageLoopProxy>
+BrowserThread::GetMessageLoopProxyForThread(
+ ID identifier) {
+ scoped_refptr<base::MessageLoopProxy> proxy(
+ new BrowserThreadMessageLoopProxy(identifier));
+ return proxy;
+}
+
+// static
+bool BrowserThread::PostTaskHelper(
+ ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task,
+ int64 delay_ms,
+ bool nestable) {
+ DCHECK(identifier >= 0 && identifier < ID_COUNT);
+ // Optimization: to avoid unnecessary locks, we listed the ID enumeration in
+ // order of lifetime. So no need to lock if we know that the other thread
+ // outlives this one.
+ // Note: since the array is so small, ok to loop instead of creating a map,
+ // which would require a lock because std::map isn't thread safe, defeating
+ // the whole purpose of this optimization.
+ ID current_thread;
+ bool guaranteed_to_outlive_target_thread =
+ GetCurrentThreadIdentifier(&current_thread) &&
+ current_thread >= identifier;
+
+ if (!guaranteed_to_outlive_target_thread)
+ lock_.Acquire();
+
+ MessageLoop* message_loop = browser_threads_[identifier] ?
+ browser_threads_[identifier]->message_loop() : NULL;
+ if (message_loop) {
+ if (nestable) {
+ message_loop->PostDelayedTask(from_here, task, delay_ms);
+ } else {
+ message_loop->PostNonNestableDelayedTask(from_here, task, delay_ms);
+ }
+ } else {
+ delete task;
+ }
+
+ if (!guaranteed_to_outlive_target_thread)
+ lock_.Release();
+
+ return !!message_loop;
+}
diff --git a/content/browser/browser_thread.h b/content/browser/browser_thread.h
new file mode 100644
index 0000000..d5e4473
--- /dev/null
+++ b/content/browser/browser_thread.h
@@ -0,0 +1,209 @@
+// 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 CONTENT_BROWSER_BROWSER_THREAD_H_
+#define CONTENT_BROWSER_BROWSER_THREAD_H_
+#pragma once
+
+#include "base/synchronization/lock.h"
+#include "base/task.h"
+#include "base/threading/thread.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BrowserThread
+//
+// This class represents a thread that is known by a browser-wide name. For
+// example, there is one IO thread for the entire browser process, and various
+// pieces of code find it useful to retrieve a pointer to the IO thread's
+// Invoke a task by thread ID:
+//
+// BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task);
+//
+// The return value is false if the task couldn't be posted because the target
+// thread doesn't exist. If this could lead to data loss, you need to check the
+// result and restructure the code to ensure it doesn't occur.
+//
+// This class automatically handles the lifetime of different threads.
+// It's always safe to call PostTask on any thread. If it's not yet created,
+// the task is deleted. There are no race conditions. If the thread that the
+// task is posted to is guaranteed to outlive the current thread, then no locks
+// are used. You should never need to cache pointers to MessageLoops, since
+// they're not thread safe.
+class BrowserThread : public base::Thread {
+ public:
+ // An enumeration of the well-known threads.
+ // NOTE: threads must be listed in the order of their life-time, with each
+ // thread outliving every other thread below it.
+ enum ID {
+ // The main thread in the browser.
+ UI,
+
+ // This is the thread that interacts with the database.
+ DB,
+
+ // This is the "main" thread for WebKit within the browser process when
+ // NOT in --single-process mode.
+ WEBKIT,
+
+ // This is the thread that interacts with the file system.
+ FILE,
+
+ // Used to launch and terminate processes.
+ PROCESS_LAUNCHER,
+
+ // This is the thread to handle slow HTTP cache operations.
+ CACHE,
+
+ // This is the thread that processes IPC and network messages.
+ IO,
+
+#if defined(USE_X11)
+ // This thread has a second connection to the X server and is used to
+ // process UI requests when routing the request to the UI thread would risk
+ // deadlock.
+ BACKGROUND_X11,
+#endif
+
+ // This identifier does not represent a thread. Instead it counts the
+ // number of well-known threads. Insert new well-known threads before this
+ // identifier.
+ ID_COUNT
+ };
+
+ // Construct a BrowserThread with the supplied identifier. It is an error
+ // to construct a BrowserThread that already exists.
+ explicit BrowserThread(ID identifier);
+
+ // Special constructor for the main (UI) thread and unittests. We use a dummy
+ // thread here since the main thread already exists.
+ BrowserThread(ID identifier, MessageLoop* message_loop);
+
+ virtual ~BrowserThread();
+
+ // These are the same methods in message_loop.h, but are guaranteed to either
+ // get posted to the MessageLoop if it's still alive, or be deleted otherwise.
+ // They return true iff the thread existed and the task was posted. Note that
+ // even if the task is posted, there's no guarantee that it will run, since
+ // the target thread may already have a Quit message in its queue.
+ static bool PostTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task);
+ static bool PostDelayedTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task,
+ int64 delay_ms);
+ static bool PostNonNestableTask(ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task);
+ static bool PostNonNestableDelayedTask(
+ ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task,
+ int64 delay_ms);
+
+ template <class T>
+ static bool DeleteSoon(ID identifier,
+ const tracked_objects::Location& from_here,
+ const T* object) {
+ return PostNonNestableTask(
+ identifier, from_here, new DeleteTask<T>(object));
+ }
+
+ template <class T>
+ static bool ReleaseSoon(ID identifier,
+ const tracked_objects::Location& from_here,
+ const T* object) {
+ return PostNonNestableTask(
+ identifier, from_here, new ReleaseTask<T>(object));
+ }
+
+ // Callable on any thread. Returns whether the given ID corresponds to a well
+ // known thread.
+ static bool IsWellKnownThread(ID identifier);
+
+ // Callable on any thread. Returns whether you're currently on a particular
+ // thread.
+ static bool CurrentlyOn(ID identifier);
+
+ // Callable on any thread. Returns whether the threads message loop is valid.
+ // If this returns false it means the thread is in the process of shutting
+ // down.
+ static bool IsMessageLoopValid(ID identifier);
+
+ // If the current message loop is one of the known threads, returns true and
+ // sets identifier to its ID. Otherwise returns false.
+ static bool GetCurrentThreadIdentifier(ID* identifier);
+
+ // Callers can hold on to a refcounted MessageLoopProxy beyond the lifetime
+ // of the thread.
+ static scoped_refptr<base::MessageLoopProxy> GetMessageLoopProxyForThread(
+ ID identifier);
+
+ // Use these templates in conjuction with RefCountedThreadSafe when you want
+ // to ensure that an object is deleted on a specific thread. This is needed
+ // when an object can hop between threads (i.e. IO -> FILE -> IO), and thread
+ // switching delays can mean that the final IO tasks executes before the FILE
+ // task's stack unwinds. This would lead to the object destructing on the
+ // FILE thread, which often is not what you want (i.e. to unregister from
+ // NotificationService, to notify other objects on the creating thread etc).
+ template<ID thread>
+ struct DeleteOnThread {
+ template<typename T>
+ static void Destruct(const T* x) {
+ if (CurrentlyOn(thread)) {
+ delete x;
+ } else {
+ DeleteSoon(thread, FROM_HERE, x);
+ }
+ }
+ };
+
+ // Sample usage:
+ // class Foo
+ // : public base::RefCountedThreadSafe<
+ // Foo, BrowserThread::DeleteOnIOThread> {
+ //
+ // ...
+ // private:
+ // friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
+ // friend class DeleteTask<Foo>;
+ //
+ // ~Foo();
+ struct DeleteOnUIThread : public DeleteOnThread<UI> { };
+ struct DeleteOnIOThread : public DeleteOnThread<IO> { };
+ struct DeleteOnFileThread : public DeleteOnThread<FILE> { };
+ struct DeleteOnDBThread : public DeleteOnThread<DB> { };
+ struct DeleteOnWebKitThread : public DeleteOnThread<WEBKIT> { };
+
+ private:
+ // Common initialization code for the constructors.
+ void Initialize();
+
+ static bool PostTaskHelper(
+ ID identifier,
+ const tracked_objects::Location& from_here,
+ Task* task,
+ int64 delay_ms,
+ bool nestable);
+
+ // The identifier of this thread. Only one thread can exist with a given
+ // identifier at a given time.
+ ID identifier_;
+
+ // This lock protects |browser_threads_|. Do not read or modify that array
+ // without holding this lock. Do not block while holding this lock.
+ static base::Lock lock_;
+
+ // An array of the BrowserThread objects. This array is protected by |lock_|.
+ // The threads are not owned by this array. Typically, the threads are owned
+ // on the UI thread by the g_browser_process object. BrowserThreads remove
+ // themselves from this array upon destruction.
+ static BrowserThread* browser_threads_[ID_COUNT];
+};
+
+#endif // CONTENT_BROWSER_BROWSER_THREAD_H_
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc
new file mode 100644
index 0000000..af2a869
--- /dev/null
+++ b/content/browser/browser_thread_unittest.cc
@@ -0,0 +1,166 @@
+// 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 "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/scoped_ptr.h"
+#include "content/browser/browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+class BrowserThreadTest : public testing::Test {
+ public:
+ void Release() const {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask);
+ }
+
+ protected:
+ virtual void SetUp() {
+ ui_thread_.reset(new BrowserThread(BrowserThread::UI));
+ file_thread_.reset(new BrowserThread(BrowserThread::FILE));
+ ui_thread_->Start();
+ file_thread_->Start();
+ }
+
+ virtual void TearDown() {
+ ui_thread_->Stop();
+ file_thread_->Stop();
+ }
+
+ static void BasicFunction(MessageLoop* message_loop) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask);
+ }
+
+ class DummyTask : public Task {
+ public:
+ explicit DummyTask(bool* deleted) : deleted_(deleted) { }
+ ~DummyTask() {
+ *deleted_ = true;
+ }
+
+ void Run() {
+ CHECK(false);
+ }
+
+ private:
+ bool* deleted_;
+ };
+
+ class DeletedOnFile
+ : public base::RefCountedThreadSafe<
+ DeletedOnFile, BrowserThread::DeleteOnFileThread> {
+ public:
+ explicit DeletedOnFile(MessageLoop* message_loop)
+ : message_loop_(message_loop) { }
+
+ ~DeletedOnFile() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+ }
+
+ private:
+ MessageLoop* message_loop_;
+ };
+
+ class NeverDeleted
+ : public base::RefCountedThreadSafe<
+ NeverDeleted, BrowserThread::DeleteOnWebKitThread> {
+ public:
+ ~NeverDeleted() {
+ CHECK(false);
+ }
+ };
+
+ private:
+ scoped_ptr<BrowserThread> ui_thread_;
+ scoped_ptr<BrowserThread> file_thread_;
+ // It's kind of ugly to make this mutable - solely so we can post the Quit
+ // Task from Release(). This should be fixed.
+ mutable MessageLoop loop_;
+};
+
+TEST_F(BrowserThreadTest, PostTask) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(&BasicFunction, MessageLoop::current()));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(BrowserThreadTest, Release) {
+ BrowserThread::ReleaseSoon(BrowserThread::UI, FROM_HERE, this);
+ MessageLoop::current()->Run();
+}
+
+TEST_F(BrowserThreadTest, TaskToNonExistentThreadIsDeleted) {
+ bool deleted = false;
+ BrowserThread::PostTask(
+ BrowserThread::WEBKIT, FROM_HERE,
+ new DummyTask(&deleted));
+ EXPECT_TRUE(deleted);
+}
+
+TEST_F(BrowserThreadTest, ReleasedOnCorrectThread) {
+ {
+ scoped_refptr<DeletedOnFile> test(
+ new DeletedOnFile(MessageLoop::current()));
+ }
+ MessageLoop::current()->Run();
+}
+
+TEST_F(BrowserThreadTest, NotReleasedIfTargetThreadNonExistent) {
+ scoped_refptr<NeverDeleted> test(new NeverDeleted());
+}
+
+TEST_F(BrowserThreadTest, PostTaskViaMessageLoopProxy) {
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
+ message_loop_proxy->PostTask(FROM_HERE,
+ NewRunnableFunction(&BasicFunction,
+ MessageLoop::current()));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(BrowserThreadTest, ReleaseViaMessageLoopProxy) {
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
+ message_loop_proxy->ReleaseSoon(FROM_HERE, this);
+ MessageLoop::current()->Run();
+}
+
+TEST_F(BrowserThreadTest, TaskToNonExistentThreadIsDeletedViaMessageLoopProxy) {
+ bool deleted = false;
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::WEBKIT);
+ message_loop_proxy->PostTask(FROM_HERE, new DummyTask(&deleted));
+ EXPECT_TRUE(deleted);
+}
+
+TEST_F(BrowserThreadTest, PostTaskViaMessageLoopProxyAfterThreadExits) {
+ scoped_ptr<BrowserThread> io_thread(new BrowserThread(BrowserThread::IO));
+ io_thread->Start();
+ io_thread->Stop();
+
+ bool deleted = false;
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
+ bool ret = message_loop_proxy->PostTask(FROM_HERE, new DummyTask(&deleted));
+ EXPECT_FALSE(ret);
+ EXPECT_TRUE(deleted);
+}
+
+TEST_F(BrowserThreadTest, PostTaskViaMessageLoopProxyAfterThreadIsDeleted) {
+ {
+ scoped_ptr<BrowserThread> io_thread(new BrowserThread(BrowserThread::IO));
+ io_thread->Start();
+ }
+ bool deleted = false;
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
+ bool ret = message_loop_proxy->PostTask(FROM_HERE, new DummyTask(&deleted));
+ EXPECT_FALSE(ret);
+ EXPECT_TRUE(deleted);
+}
+
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
new file mode 100644
index 0000000..b2c514c
--- /dev/null
+++ b/content/browser/browsing_instance.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2011 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/browsing_instance.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/webui/web_ui_factory.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/site_instance.h"
+
+// static
+BrowsingInstance::ProfileSiteInstanceMap
+ BrowsingInstance::profile_site_instance_map_;
+
+BrowsingInstance::BrowsingInstance(Profile* profile)
+ : profile_(profile) {
+}
+
+bool BrowsingInstance::ShouldUseProcessPerSite(const GURL& url) {
+ // Returns true if we should use the process-per-site model. This will be
+ // the case if the --process-per-site switch is specified, or in
+ // process-per-site-instance for particular sites (e.g., the new tab page).
+
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kProcessPerSite))
+ return true;
+
+ // We want to consolidate particular sites like extensions and WebUI whether
+ // it is in process-per-tab or process-per-site-instance.
+ // Note that --single-process may have been specified, but that affects the
+ // process creation logic in RenderProcessHost, so we do not need to worry
+ // about it here.
+
+ if (url.SchemeIs(chrome::kExtensionScheme))
+ return true;
+
+ // DevTools pages have WebUI type but should not reuse the same host.
+ if (WebUIFactory::UseWebUIForURL(profile_, url) &&
+ !url.SchemeIs(chrome::kChromeDevToolsScheme))
+ return true;
+
+ // In all other cases, don't use process-per-site logic.
+ return false;
+}
+
+BrowsingInstance::SiteInstanceMap* BrowsingInstance::GetSiteInstanceMap(
+ Profile* profile, const GURL& url) {
+ if (!ShouldUseProcessPerSite(SiteInstance::GetEffectiveURL(profile, url))) {
+ // Not using process-per-site, so use a map specific to this instance.
+ return &site_instance_map_;
+ }
+
+ // Otherwise, process-per-site is in use, at least for this URL. Look up the
+ // global map for this profile, creating an entry if necessary.
+ ProfileId runtime_id = profile ? profile->GetRuntimeId()
+ : Profile::InvalidProfileId;
+ return &profile_site_instance_map_[runtime_id];
+}
+
+bool BrowsingInstance::HasSiteInstance(const GURL& url) {
+ std::string site =
+ SiteInstance::GetSiteForURL(profile_, url).possibly_invalid_spec();
+
+ SiteInstanceMap* map = GetSiteInstanceMap(profile_, url);
+ SiteInstanceMap::iterator i = map->find(site);
+ return (i != map->end());
+}
+
+SiteInstance* BrowsingInstance::GetSiteInstanceForURL(const GURL& url) {
+ std::string site =
+ SiteInstance::GetSiteForURL(profile_, url).possibly_invalid_spec();
+
+ SiteInstanceMap* map = GetSiteInstanceMap(profile_, url);
+ SiteInstanceMap::iterator i = map->find(site);
+ if (i != map->end()) {
+ return i->second;
+ }
+
+ // No current SiteInstance for this site, so let's create one.
+ SiteInstance* instance = new SiteInstance(this);
+
+ // Set the site of this new SiteInstance, which will register it with us.
+ instance->SetSite(url);
+ return instance;
+}
+
+void BrowsingInstance::RegisterSiteInstance(SiteInstance* site_instance) {
+ DCHECK(site_instance->browsing_instance() == this);
+ DCHECK(site_instance->has_site());
+ std::string site = site_instance->site().possibly_invalid_spec();
+
+ // Only register if we don't have a SiteInstance for this site already.
+ // It's possible to have two SiteInstances point to the same site if two
+ // tabs are navigated there at the same time. (We don't call SetSite or
+ // register them until DidNavigate.) If there is a previously existing
+ // SiteInstance for this site, we just won't register the new one.
+ SiteInstanceMap* map = GetSiteInstanceMap(profile_, site_instance->site());
+ SiteInstanceMap::iterator i = map->find(site);
+ if (i == map->end()) {
+ // Not previously registered, so register it.
+ (*map)[site] = site_instance;
+ }
+}
+
+void BrowsingInstance::UnregisterSiteInstance(SiteInstance* site_instance) {
+ DCHECK(site_instance->browsing_instance() == this);
+ DCHECK(site_instance->has_site());
+ std::string site = site_instance->site().possibly_invalid_spec();
+
+ // Only unregister the SiteInstance if it is the same one that is registered
+ // for the site. (It might have been an unregistered SiteInstance. See the
+ // comments in RegisterSiteInstance.)
+
+ // We look for the site instance in both the local site_instance_map_ and also
+ // the static profile_site_instance_map_ - this is because the logic in
+ // ShouldUseProcessPerSite() can produce different results over the lifetime
+ // of Chrome (e.g. installation of apps with web extents can change our
+ // process-per-site policy for a given domain), so we don't know which map
+ // the site was put into when it was originally registered.
+ if (!RemoveSiteInstanceFromMap(&site_instance_map_, site, site_instance)) {
+ // Wasn't in our local map, so look in the static per-profile map.
+ ProfileId runtime_id = profile_ ? profile_->GetRuntimeId()
+ : Profile::InvalidProfileId;
+ RemoveSiteInstanceFromMap(
+ &profile_site_instance_map_[runtime_id], site, site_instance);
+ }
+}
+
+bool BrowsingInstance::RemoveSiteInstanceFromMap(SiteInstanceMap* map,
+ const std::string& site,
+ SiteInstance* site_instance) {
+ SiteInstanceMap::iterator i = map->find(site);
+ if (i != map->end() && i->second == site_instance) {
+ // Matches, so erase it.
+ map->erase(i);
+ return true;
+ }
+ return false;
+}
+
+BrowsingInstance::~BrowsingInstance() {
+ // We should only be deleted when all of the SiteInstances that refer to
+ // us are gone.
+ DCHECK(site_instance_map_.empty());
+}
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h
new file mode 100644
index 0000000..0c4bb2e
--- /dev/null
+++ b/content/browser/browsing_instance.h
@@ -0,0 +1,135 @@
+// 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 CONTENT_BROWSER_BROWSING_INSTANCE_H_
+#define CONTENT_BROWSER_BROWSING_INSTANCE_H_
+#pragma once
+
+#include "base/hash_tables.h"
+#include "base/ref_counted.h"
+#include "chrome/browser/profiles/profile.h"
+
+class GURL;
+class SiteInstance;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// BrowsingInstance class
+//
+// A browsing instance corresponds to the notion of a "unit of related browsing
+// contexts" in the HTML 5 spec. Intuitively, it represents a collection of
+// tabs and frames that can have script connections to each other. In that
+// sense, it reflects the user interface, and not the contents of the tabs and
+// frames.
+//
+// We further subdivide a BrowsingInstance into SiteInstances, which represent
+// the documents within each BrowsingInstance that are from the same site and
+// thus can have script access to each other. Different SiteInstances can
+// safely run in different processes, because their documents cannot access
+// each other's contents (due to the same origin policy).
+//
+// It is important to only have one SiteInstance per site within a given
+// BrowsingInstance. This is because any two documents from the same site
+// might be able to script each other if they are in the same BrowsingInstance.
+// Thus, they must be rendered in the same process.
+//
+// If the process-per-site model is in use, then we ensure that there is only
+// one SiteInstance per site for the entire profile, not just for each
+// BrowsingInstance. This reduces the number of renderer processes we create.
+// (This is currently only true if --process-per-site is specified at the
+// command line.)
+//
+// A BrowsingInstance is live as long as any SiteInstance has a reference to
+// it. A SiteInstance is live as long as any NavigationEntry or RenderViewHost
+// have references to it. Because both classes are RefCounted, they do not
+// need to be manually deleted.
+//
+// Currently, the BrowsingInstance class is not visible outside of the
+// SiteInstance class. To get a new SiteInstance that is part of the same
+// BrowsingInstance, use SiteInstance::GetRelatedSiteInstance. Because of
+// this, BrowsingInstances and SiteInstances are tested together in
+// site_instance_unittest.cc.
+//
+///////////////////////////////////////////////////////////////////////////////
+class BrowsingInstance : public base::RefCounted<BrowsingInstance> {
+ public:
+ // Create a new BrowsingInstance.
+ explicit BrowsingInstance(Profile* profile);
+
+ // Returns whether the process-per-site model is in use (globally or just for
+ // the given url), in which case we should ensure there is only one
+ // SiteInstance per site for the entire profile, not just for this
+ // BrowsingInstance.
+ virtual bool ShouldUseProcessPerSite(const GURL& url);
+
+ // Get the profile to which this BrowsingInstance belongs.
+ Profile* profile() { return profile_; }
+
+ // Returns whether this BrowsingInstance has registered a SiteInstance for
+ // the site of the given URL.
+ bool HasSiteInstance(const GURL& url);
+
+ // Get the SiteInstance responsible for rendering the given URL. Should
+ // create a new one if necessary, but should not create more than one
+ // SiteInstance per site.
+ SiteInstance* GetSiteInstanceForURL(const GURL& url);
+
+ // Adds the given SiteInstance to our map, to ensure that we do not create
+ // another SiteInstance for the same site.
+ void RegisterSiteInstance(SiteInstance* site_instance);
+
+ // Removes the given SiteInstance from our map, after all references to it
+ // have been deleted. This means it is safe to create a new SiteInstance
+ // if the user later visits a page from this site, within this
+ // BrowsingInstance.
+ void UnregisterSiteInstance(SiteInstance* site_instance);
+
+ protected:
+ friend class base::RefCounted<BrowsingInstance>;
+
+ // Virtual to allow tests to extend it.
+ virtual ~BrowsingInstance();
+
+ private:
+ // Map of site to SiteInstance, to ensure we only have one SiteInstance per
+ // site. The site string should be the possibly_invalid_spec() of a GURL
+ // obtained with SiteInstance::GetSiteForURL.
+ typedef base::hash_map<std::string, SiteInstance*> SiteInstanceMap;
+
+ // Map of Profile runtime Id to SiteInstanceMap, for use in the
+ // process-per-site model.
+ typedef base::hash_map<ProfileId, SiteInstanceMap> ProfileSiteInstanceMap;
+
+ // Returns a pointer to the relevant SiteInstanceMap for this object. If the
+ // process-per-site model is in use, or if process-per-site-instance is in
+ // use and |url| matches a site for which we always use one process (e.g.,
+ // the new tab page), then this returns the SiteInstanceMap for the entire
+ // profile. If not, this returns the BrowsingInstance's own private
+ // SiteInstanceMap.
+ SiteInstanceMap* GetSiteInstanceMap(Profile* profile, const GURL& url);
+
+ // Utility routine which removes the passed SiteInstance from the passed
+ // SiteInstanceMap.
+ bool RemoveSiteInstanceFromMap(SiteInstanceMap* map, const std::string& site,
+ SiteInstance* site_instance);
+
+ // Common profile to which all SiteInstances in this BrowsingInstance
+ // must belong.
+ Profile* const profile_;
+
+ // Map of site to SiteInstance, to ensure we only have one SiteInstance per
+ // site. The site string should be the possibly_invalid_spec() of a GURL
+ // obtained with SiteInstance::GetSiteForURL. Note that this map may not
+ // contain every active SiteInstance, because a race exists where two
+ // SiteInstances can be assigned to the same site. This is ok in rare cases.
+ // This field is only used if we are not using process-per-site.
+ SiteInstanceMap site_instance_map_;
+
+ // Global map of Profile to SiteInstanceMap, for process-per-site.
+ static ProfileSiteInstanceMap profile_site_instance_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingInstance);
+};
+
+#endif // CONTENT_BROWSER_BROWSING_INSTANCE_H_
diff --git a/content/browser/cancelable_request.cc b/content/browser/cancelable_request.cc
new file mode 100644
index 0000000..f1826fd
--- /dev/null
+++ b/content/browser/cancelable_request.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2011 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/cancelable_request.h"
+
+CancelableRequestProvider::CancelableRequestProvider() : next_handle_(1) {
+}
+
+CancelableRequestProvider::~CancelableRequestProvider() {
+ // There may be requests whose result callback has not been run yet. We need
+ // to cancel them otherwise they may try and call us back after we've been
+ // deleted, or do other bad things. This can occur on shutdown (or profile
+ // destruction) when a request is scheduled, completed (but not dispatched),
+ // then the Profile is deleted.
+ base::AutoLock lock(pending_request_lock_);
+ while (!pending_requests_.empty())
+ CancelRequestLocked(pending_requests_.begin());
+}
+
+CancelableRequestProvider::Handle CancelableRequestProvider::AddRequest(
+ CancelableRequestBase* request,
+ CancelableRequestConsumerBase* consumer) {
+ Handle handle;
+ {
+ base::AutoLock lock(pending_request_lock_);
+
+ handle = next_handle_;
+ pending_requests_[next_handle_] = request;
+ ++next_handle_;
+ }
+
+ consumer->OnRequestAdded(this, handle);
+
+ request->Init(this, handle, consumer);
+ return handle;
+}
+
+void CancelableRequestProvider::CancelRequest(Handle handle) {
+ base::AutoLock lock(pending_request_lock_);
+ CancelRequestLocked(pending_requests_.find(handle));
+}
+
+void CancelableRequestProvider::CancelRequestLocked(
+ const CancelableRequestMap::iterator& item) {
+ pending_request_lock_.AssertAcquired();
+ if (item == pending_requests_.end()) {
+ NOTREACHED() << "Trying to cancel an unknown request";
+ return;
+ }
+
+ item->second->consumer()->OnRequestRemoved(this, item->first);
+ item->second->set_canceled();
+ pending_requests_.erase(item);
+}
+
+void CancelableRequestProvider::RequestCompleted(Handle handle) {
+ CancelableRequestConsumerBase* consumer = NULL;
+ {
+ base::AutoLock lock(pending_request_lock_);
+
+ CancelableRequestMap::iterator i = pending_requests_.find(handle);
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Trying to cancel an unknown request";
+ return;
+ }
+ consumer = i->second->consumer();
+
+ // This message should only get sent if the class is not cancelled, or
+ // else the consumer might be gone).
+ DCHECK(!i->second->canceled());
+
+ pending_requests_.erase(i);
+ }
+
+ // Notify the consumer that the request is gone
+ consumer->OnRequestRemoved(this, handle);
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Emit our most common CancelableRequestConsumer.
+template class CancelableRequestConsumerTSimple<int>;
+
+// And the most common subclass of it.
+template class CancelableRequestConsumerT<int, 0>;
+#endif
+
+CancelableRequestBase::CancelableRequestBase()
+ : provider_(NULL),
+ consumer_(NULL),
+ handle_(0) {
+ callback_thread_ = MessageLoop::current();
+}
+
+CancelableRequestBase::~CancelableRequestBase() {
+}
+
+void CancelableRequestBase::Init(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle,
+ CancelableRequestConsumerBase* consumer) {
+ DCHECK(handle_ == 0 && provider_ == NULL && consumer_ == NULL);
+ provider_ = provider;
+ consumer_ = consumer;
+ handle_ = handle;
+}
diff --git a/content/browser/cancelable_request.h b/content/browser/cancelable_request.h
new file mode 100644
index 0000000..9dc86d3
--- /dev/null
+++ b/content/browser/cancelable_request.h
@@ -0,0 +1,704 @@
+// 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.
+
+// CancelableRequestProviders and Consumers work together to make requests that
+// execute on a background thread in the provider and return data to the
+// consumer. These class collaborate to keep a list of open requests and to
+// make sure that requests to not outlive either of the objects involved in the
+// transaction.
+//
+// If you do not need to return data to the consumer, do not use this system,
+// just use the regular Task/RunnableMethod stuff.
+//
+// The CancelableRequest object is used internally to each provider to track
+// request data and callback information.
+//
+// Example consumer calling |StartRequest| on a frontend service:
+//
+// class MyClass {
+// void MakeRequest() {
+// frontend_service->StartRequest(some_input1, some_input2,
+// &callback_consumer_,
+// NewCallback(this, &MyClass:RequestComplete));
+// // StartRequest() returns a Handle which may be retained for use with
+// // CancelRequest() if required, e.g. in MyClass's destructor.
+// }
+//
+// void RequestComplete(int status) {
+// ...
+// }
+//
+// private:
+// CancelableRequestConsumer callback_consumer_;
+// };
+//
+//
+// Example frontend provider. It receives requests and forwards them to the
+// backend on another thread:
+//
+// class Frontend : public CancelableRequestProvider {
+// typedef Callback1<int>::Type RequestCallbackType;
+//
+// Handle StartRequest(int some_input1, int some_input2,
+// CancelableRequestConsumerBase* consumer,
+// RequestCallbackType* callback) {
+// scoped_refptr<CancelableRequest<RequestCallbackType> > request(
+// new CancelableRequest<RequestCallbackType>(callback));
+// AddRequest(request, consumer);
+//
+// // Send the parameters and the request to the backend thread.
+// backend_thread_->PostTask(FROM_HERE,
+// NewRunnableMethod(backend_, &Backend::DoRequest, request,
+// some_input1, some_input2));
+//
+// // The handle will have been set by AddRequest.
+// return request->handle();
+// }
+// };
+//
+//
+// Example backend provider that does work and dispatches the callback back
+// to the original thread. Note that we need to pass it as a scoped_refptr so
+// that the object will be kept alive if the request is canceled (releasing
+// the provider's reference to it).
+//
+// class Backend {
+// void DoRequest(
+// scoped_refptr< CancelableRequest<Frontend::RequestCallbackType> >
+// request,
+// int some_input1, int some_input2) {
+// if (request->canceled())
+// return;
+//
+// ... do your processing ...
+//
+// // Depending on your typedefs, one of these two forms will be more
+// // convenient:
+// request->ForwardResult(Tuple1<int>(return_value));
+//
+// // -- or -- (inferior in this case)
+// request->ForwardResult(Frontend::RequestCallbackType::TupleType(
+// return_value));
+// }
+// };
+
+#ifndef CONTENT_BROWSER_CANCELABLE_REQUEST_H_
+#define CONTENT_BROWSER_CANCELABLE_REQUEST_H_
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/lock.h"
+#include "base/task.h"
+#include "build/build_config.h"
+
+class CancelableRequestBase;
+class CancelableRequestConsumerBase;
+
+// CancelableRequestProvider --------------------------------------------------
+//
+// This class is threadsafe. Requests may be added or canceled from any thread,
+// but a task must only be canceled from the same thread it was initially run
+// on.
+//
+// It is intended that providers inherit from this class to provide the
+// necessary functionality.
+
+class CancelableRequestProvider {
+ public:
+ // Identifies a specific request from this provider.
+ typedef int Handle;
+
+ CancelableRequestProvider();
+ virtual ~CancelableRequestProvider();
+
+ // Called by the enduser of the request to cancel it. This MUST be called on
+ // the same thread that originally issued the request (which is also the same
+ // thread that would have received the callback if it was not canceled).
+ // handle must be for a valid pending (not yet complete or cancelled) request.
+ void CancelRequest(Handle handle);
+
+ protected:
+ // Adds a new request and initializes it. This is called by a derived class
+ // to add a new request. The request's Init() will be called (which is why
+ // the consumer is required. The handle to the new request is returned.
+ Handle AddRequest(CancelableRequestBase* request,
+ CancelableRequestConsumerBase* consumer);
+
+ // Called by the CancelableRequest when the request has executed. It will
+ // be removed from the list of pending requests (as opposed to canceling,
+ // which will also set some state on the request).
+ void RequestCompleted(Handle handle);
+
+ private:
+ typedef std::map<Handle, scoped_refptr<CancelableRequestBase> >
+ CancelableRequestMap;
+
+ // Only call this when you already have acquired pending_request_lock_.
+ void CancelRequestLocked(const CancelableRequestMap::iterator& item);
+
+ friend class CancelableRequestBase;
+
+ base::Lock pending_request_lock_;
+
+ // Lists all outstanding requests. Protected by the |lock_|.
+ CancelableRequestMap pending_requests_;
+
+ // The next handle value we will return. Protected by the |lock_|.
+ int next_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(CancelableRequestProvider);
+};
+
+// CancelableRequestConsumer --------------------------------------------------
+//
+// Classes wishing to make requests on a provider should have an instance of
+// this class. Callers will need to pass a pointer to this consumer object
+// when they make the request. It will automatically track any pending
+// requests, and will automatically cancel them on destruction to prevent the
+// accidental calling of freed memory.
+//
+// It is recommended to just have this class as a member variable since there
+// is nothing to be gained by inheriting from it other than polluting your
+// namespace.
+//
+// THIS CLASS IS NOT THREADSAFE (unlike the provider). You must make requests
+// and get callbacks all from the same thread.
+
+// Base class used to notify of new requests.
+class CancelableRequestConsumerBase {
+ protected:
+ friend class CancelableRequestBase;
+ friend class CancelableRequestProvider;
+
+ virtual ~CancelableRequestConsumerBase() {
+ }
+
+ // Adds a new request to the list of requests that are being tracked. This
+ // is called by the provider when a new request is created.
+ virtual void OnRequestAdded(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) = 0;
+
+ // Removes the given request from the list of pending requests. Called
+ // by the CancelableRequest immediately after the callback has executed for a
+ // given request, and by the provider when a request is canceled.
+ virtual void OnRequestRemoved(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) = 0;
+
+ // Sent to provider before executing a callback.
+ virtual void WillExecute(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) = 0;
+
+ // Sent after executing a callback.
+ virtual void DidExecute(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) = 0;
+};
+
+// Template for clients to use. It allows them to associate random "client
+// data" with a specific request. The default value for this type is 0.
+// The type T should be small and easily copyable (like a pointer
+// or an integer).
+template<class T>
+class CancelableRequestConsumerTSimple : public CancelableRequestConsumerBase {
+ public:
+ CancelableRequestConsumerTSimple();
+
+ // Cancel any outstanding requests so that we do not get called back after we
+ // are destroyed. As these requests are removed, the providers will call us
+ // back on OnRequestRemoved, which will then update the list. To iterate
+ // successfully while the list is changing out from under us, we make a copy.
+ virtual ~CancelableRequestConsumerTSimple();
+
+ // Associates some random data with a specified request. The request MUST be
+ // outstanding, or it will assert. This is intended to be called immediately
+ // after a request is issued.
+ void SetClientData(CancelableRequestProvider* p,
+ CancelableRequestProvider::Handle h,
+ T client_data);
+
+ // Retrieves previously associated data for a specified request. The request
+ // MUST be outstanding, or it will assert. This is intended to be called
+ // during processing of a callback to retrieve extra data.
+ T GetClientData(CancelableRequestProvider* p,
+ CancelableRequestProvider::Handle h);
+
+ // Returns the data associated with the current request being processed. This
+ // is only valid during the time a callback is being processed.
+ T GetClientDataForCurrentRequest();
+
+ // Returns true if there are any pending requests.
+ bool HasPendingRequests() const;
+
+ // Returns the number of pending requests.
+ size_t PendingRequestCount() const;
+
+ // Cancels all requests outstanding.
+ void CancelAllRequests();
+
+ // Returns the handle for the first request that has the specified client data
+ // (in |handle|). Returns true if there is a request for the specified client
+ // data, false otherwise.
+ bool GetFirstHandleForClientData(T client_data,
+ CancelableRequestProvider::Handle* handle);
+
+ // Gets the client data for all pending requests.
+ void GetAllClientData(std::vector<T>* data);
+
+ protected:
+ struct PendingRequest {
+ PendingRequest(CancelableRequestProvider* p,
+ CancelableRequestProvider::Handle h)
+ : provider(p), handle(h) {
+ }
+
+ PendingRequest() : provider(NULL), handle(0) {}
+
+ // Comparison operator for stl.
+ bool operator<(const PendingRequest& other) const {
+ if (provider != other.provider)
+ return provider < other.provider;
+ return handle < other.handle;
+ }
+
+ bool is_valid() const { return provider != NULL; }
+
+ CancelableRequestProvider* provider;
+ CancelableRequestProvider::Handle handle;
+ };
+ typedef std::map<PendingRequest, T> PendingRequestList;
+
+ virtual T get_initial_t() const;
+
+ virtual void OnRequestAdded(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle);
+
+ virtual void OnRequestRemoved(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle);
+
+ virtual void WillExecute(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle);
+
+ virtual void DidExecute(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle);
+
+ // Lists all outstanding requests.
+ PendingRequestList pending_requests_;
+
+ // This is valid while processing a request and is used to identify the
+ // provider/handle of request.
+ PendingRequest current_request_;
+};
+
+template<class T>
+CancelableRequestConsumerTSimple<T>::CancelableRequestConsumerTSimple() {
+}
+
+template<class T>
+CancelableRequestConsumerTSimple<T>::~CancelableRequestConsumerTSimple() {
+ CancelAllRequests();
+}
+
+template<class T>
+void CancelableRequestConsumerTSimple<T>::SetClientData(
+ CancelableRequestProvider* p,
+ CancelableRequestProvider::Handle h,
+ T client_data) {
+ PendingRequest request(p, h);
+ DCHECK(pending_requests_.find(request) != pending_requests_.end());
+ pending_requests_[request] = client_data;
+}
+
+template<class T>
+T CancelableRequestConsumerTSimple<T>::GetClientData(
+ CancelableRequestProvider* p,
+ CancelableRequestProvider::Handle h) {
+ PendingRequest request(p, h);
+ DCHECK(pending_requests_.find(request) != pending_requests_.end());
+ return pending_requests_[request];
+}
+
+template<class T>
+T CancelableRequestConsumerTSimple<T>::GetClientDataForCurrentRequest() {
+ DCHECK(current_request_.is_valid());
+ return GetClientData(current_request_.provider, current_request_.handle);
+}
+
+template<class T>
+bool CancelableRequestConsumerTSimple<T>::HasPendingRequests() const {
+ return !pending_requests_.empty();
+}
+
+template<class T>
+size_t CancelableRequestConsumerTSimple<T>::PendingRequestCount() const {
+ return pending_requests_.size();
+}
+
+template<class T>
+void CancelableRequestConsumerTSimple<T>::CancelAllRequests() {
+ PendingRequestList copied_requests(pending_requests_);
+ for (typename PendingRequestList::iterator i = copied_requests.begin();
+ i != copied_requests.end(); ++i)
+ i->first.provider->CancelRequest(i->first.handle);
+ copied_requests.clear();
+
+ // That should have cleared all the pending items.
+ DCHECK(pending_requests_.empty());
+}
+
+template<class T>
+bool CancelableRequestConsumerTSimple<T>::GetFirstHandleForClientData(
+ T client_data,
+ CancelableRequestProvider::Handle* handle) {
+ for (typename PendingRequestList::const_iterator i =
+ pending_requests_.begin(); i != pending_requests_.end(); ++i) {
+ if (i->second == client_data) {
+ *handle = i->first.handle;
+ return true;
+ }
+ }
+ *handle = 0;
+ return false;
+}
+
+template<class T>
+void CancelableRequestConsumerTSimple<T>::GetAllClientData(
+ std::vector<T>* data) {
+ DCHECK(data);
+ for (typename PendingRequestList::iterator i = pending_requests_.begin();
+ i != pending_requests_.end(); ++i)
+ data->push_back(i->second);
+}
+
+template<class T>
+T CancelableRequestConsumerTSimple<T>::get_initial_t() const {
+ return 0;
+}
+
+template<class T>
+void CancelableRequestConsumerTSimple<T>::OnRequestAdded(
+ CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) {
+ DCHECK(pending_requests_.find(PendingRequest(provider, handle)) ==
+ pending_requests_.end());
+ pending_requests_[PendingRequest(provider, handle)] = get_initial_t();
+}
+
+template<class T>
+void CancelableRequestConsumerTSimple<T>::OnRequestRemoved(
+ CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) {
+ typename PendingRequestList::iterator i =
+ pending_requests_.find(PendingRequest(provider, handle));
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Got a complete notification for a nonexistent request";
+ return;
+ }
+
+ pending_requests_.erase(i);
+}
+
+template<class T>
+void CancelableRequestConsumerTSimple<T>::WillExecute(
+ CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) {
+ current_request_ = PendingRequest(provider, handle);
+}
+
+template<class T>
+void CancelableRequestConsumerTSimple<T>::DidExecute(
+ CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) {
+ current_request_ = PendingRequest();
+}
+
+// See CancelableRequestConsumerTSimple. The default value for T
+// is given in |initial_t|.
+template<class T, T initial_t>
+class CancelableRequestConsumerT : public CancelableRequestConsumerTSimple<T> {
+ public:
+ CancelableRequestConsumerT();
+ virtual ~CancelableRequestConsumerT();
+
+ protected:
+ virtual T get_initial_t() const;
+};
+
+template<class T, T initial_t>
+CancelableRequestConsumerT<T, initial_t>::CancelableRequestConsumerT() {
+}
+
+template<class T, T initial_t>
+CancelableRequestConsumerT<T, initial_t>::~CancelableRequestConsumerT() {
+}
+
+template<class T, T initial_t>
+T CancelableRequestConsumerT<T, initial_t>::get_initial_t() const {
+ return initial_t;
+}
+
+// Some clients may not want to store data. Rather than do some complicated
+// thing with virtual functions to allow some consumers to store extra data and
+// some not to, we just define a default one that stores some dummy data.
+typedef CancelableRequestConsumerT<int, 0> CancelableRequestConsumer;
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// The vast majority of CancelableRequestConsumers are instantiated on <int>,
+// so prevent that template from being expanded in normal code.
+extern template class CancelableRequestConsumerTSimple<int>;
+
+// We'll also want to extern-template the most common, typedef-ed
+// CancelableRequestConsumerT.
+extern template class CancelableRequestConsumerT<int, 0>;
+#endif
+
+// CancelableRequest ----------------------------------------------------------
+//
+// The request object that is used by a CancelableRequestProvider to send
+// results to a CancelableRequestConsumer. This request handles the returning
+// of results from a thread where the request is being executed to the thread
+// and callback where the results are used. IT SHOULD BE PASSED AS A
+// scoped_refptr TO KEEP IT ALIVE.
+//
+// It does not handle input parameters to the request. The caller must either
+// transfer those separately or derive from this class to add the desired
+// parameters.
+//
+// When the processing is complete on this message, the caller MUST call
+// ForwardResult() with the return arguments that will be passed to the
+// callback. If the request has been canceled, Return is optional (it will not
+// do anything). If you do not have to return to the caller, the cancelable
+// request system should not be used! (just use regular fire-and-forget tasks).
+//
+// Callback parameters are passed by value. In some cases, the request will
+// want to return a large amount of data (for example, an image). One good
+// approach is to derive from the CancelableRequest and make the data object
+// (for example, a std::vector) owned by the CancelableRequest. The pointer
+// to this data would be passed for the callback parameter. Since the
+// CancelableRequest outlives the callback call, the data will be valid on the
+// other thread for the callback, but will still be destroyed properly.
+
+// Non-templatized base class that provides cancellation
+class CancelableRequestBase
+ : public base::RefCountedThreadSafe<CancelableRequestBase> {
+ public:
+ friend class CancelableRequestProvider;
+
+ // Initializes most things to empty, Init() must be called to complete
+ // initialization of the object. This will be done by the provider when
+ // the request is dispatched.
+ //
+ // This must be called on the same thread the callback will be executed on,
+ // it will save that thread for later.
+ //
+ // This two-phase init is done so that the constructor can have no
+ // parameters, which makes it much more convenient for derived classes,
+ // which can be common. The derived classes need only declare the variables
+ // they provide in the constructor rather than many lines of internal
+ // tracking data that are passed to the base class (us).
+ //
+ // In addition, not all of the information (for example, the handle) is known
+ // at construction time.
+ CancelableRequestBase();
+
+ CancelableRequestConsumerBase* consumer() const {
+ return consumer_;
+ }
+
+ CancelableRequestProvider::Handle handle() const {
+ return handle_;
+ }
+
+ // The canceled flag indicates that the request should not be executed.
+ // A request can never be uncanceled, so only a setter for true is provided.
+ // This can be called multiple times, but only from one thread.
+ void set_canceled() {
+ canceled_.Set();
+ }
+ bool canceled() {
+ return canceled_.IsSet();
+ }
+
+ protected:
+ friend class base::RefCountedThreadSafe<CancelableRequestBase>;
+ virtual ~CancelableRequestBase();
+
+ // Initializes the object with the particulars from the provider. It may only
+ // be called once (it is called by the provider, which is a friend).
+ void Init(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle,
+ CancelableRequestConsumerBase* consumer);
+
+ // Tells the provider that the request is complete, which then tells the
+ // consumer.
+ void NotifyCompleted() const {
+ provider_->RequestCompleted(handle());
+ consumer_->DidExecute(provider_, handle_);
+ }
+
+ // Cover method for CancelableRequestConsumerBase::WillExecute.
+ void WillExecute() {
+ consumer_->WillExecute(provider_, handle_);
+ }
+
+ // The message loop that this request was created on. The callback will
+ // happen on the same thread.
+ MessageLoop* callback_thread_;
+
+ // The provider for this request. When we execute, we will notify this that
+ // request is complete to it can remove us from the requests it tracks.
+ CancelableRequestProvider* provider_;
+
+ // Notified after we execute that the request is complete. This should only
+ // be accessed if !canceled_.IsSet(), otherwise the pointer is invalid.
+ CancelableRequestConsumerBase* consumer_;
+
+ // The handle to this request inside the provider. This will be initialized
+ // to 0 when the request is created, and the provider will set it once the
+ // request has been dispatched.
+ CancelableRequestProvider::Handle handle_;
+
+ // Set if the caller cancels this request. No callbacks should be made when
+ // this is set.
+ base::CancellationFlag canceled_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CancelableRequestBase);
+};
+
+// Templatized class. This is the one you should use directly or inherit from.
+// The callback can be invoked by calling the ForwardResult() method. For this,
+// you must either pack the parameters into a tuple, or use DispatchToMethod
+// (in tuple.h).
+//
+// If you inherit to add additional input parameters or to do more complex
+// memory management (see the bigger comment about this above), you can put
+// those on a subclass of this.
+//
+// We have decided to allow users to treat derived classes of this as structs,
+// so you can add members without getters and setters (which just makes the
+// code harder to read). Don't use underscores after these vars. For example:
+//
+// typedef Callback1<int>::Type DoodieCallback;
+//
+// class DoodieRequest : public CancelableRequest<DoodieCallback> {
+// public:
+// DoodieRequest(CallbackType* callback) : CancelableRequest(callback) {
+// }
+//
+// private:
+// ~DoodieRequest() {}
+//
+// int input_arg1;
+// std::wstring input_arg2;
+// };
+template<typename CB>
+class CancelableRequest : public CancelableRequestBase {
+ public:
+ typedef CB CallbackType; // CallbackRunner<...>
+ typedef typename CB::TupleType TupleType; // Tuple of the callback args.
+
+ // The provider MUST call Init() (on the base class) before this is valid.
+ // This class will take ownership of the callback object and destroy it when
+ // appropriate.
+ explicit CancelableRequest(CallbackType* callback)
+ : CancelableRequestBase(),
+ callback_(callback) {
+ DCHECK(callback) << "We should always have a callback";
+ }
+
+ // Dispatches the parameters to the correct thread so the callback can be
+ // executed there. The caller does not need to check for cancel before
+ // calling this. It is optional in the cancelled case. In the non-cancelled
+ // case, this MUST be called.
+ //
+ // If there are any pointers in the parameters, they must live at least as
+ // long as the request so that it can be forwarded to the other thread.
+ // For complex objects, this would typically be done by having a derived
+ // request own the data itself.
+ void ForwardResult(const TupleType& param) {
+ DCHECK(callback_.get());
+ if (!canceled()) {
+ if (callback_thread_ == MessageLoop::current()) {
+ // We can do synchronous callbacks when we're on the same thread.
+ ExecuteCallback(param);
+ } else {
+ callback_thread_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &CancelableRequest<CB>::ExecuteCallback, param));
+ }
+ }
+ }
+
+ // Like |ForwardResult| but this never does a synchronous callback.
+ void ForwardResultAsync(const TupleType& param) {
+ DCHECK(callback_.get());
+ if (!canceled()) {
+ callback_thread_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &CancelableRequest<CB>::ExecuteCallback, param));
+ }
+ }
+
+ protected:
+ virtual ~CancelableRequest() {}
+
+ private:
+ // Executes the callback and notifies the provider and the consumer that this
+ // request has been completed. This must be called on the callback_thread_.
+ void ExecuteCallback(const TupleType& param) {
+ if (!canceled_.IsSet()) {
+ WillExecute();
+
+ // Execute the callback.
+ callback_->RunWithParams(param);
+
+ // Notify the provider that the request is complete. The provider will
+ // notify the consumer for us.
+ NotifyCompleted();
+ }
+ }
+
+ // This should only be executed if !canceled_.IsSet(),
+ // otherwise the pointers may be invalid.
+ scoped_ptr<CallbackType> callback_;
+};
+
+// A CancelableRequest with a single value. This is intended for use when
+// the provider provides a single value. The provider fills the result into
+// the value, and notifies the request with a pointer to the value. For example,
+// HistoryService has many methods that callback with a vector. Use the
+// following pattern for this:
+// 1. Define the callback:
+// typedef Callback2<Handle, std::vector<Foo>*>::Type FooCallback;
+// 2. Define the CancelableRequest1 type.
+// typedef CancelableRequest1<FooCallback, std::vector<Foo>> FooRequest;
+// 3. The provider method should then fillin the contents of the vector,
+// forwarding the result like so:
+// request->ForwardResult(FooRequest::TupleType(request->handle(),
+// &request->value));
+//
+// Tip: for passing more than one value, use a Tuple for the value.
+template<typename CB, typename Type>
+class CancelableRequest1 : public CancelableRequest<CB> {
+ public:
+ explicit CancelableRequest1(
+ typename CancelableRequest<CB>::CallbackType* callback)
+ : CancelableRequest<CB>(callback) {
+ }
+
+ // The value.
+ Type value;
+
+ protected:
+ virtual ~CancelableRequest1() {}
+};
+
+#endif // CONTENT_BROWSER_CANCELABLE_REQUEST_H_
diff --git a/content/browser/cert_store.cc b/content/browser/cert_store.cc
new file mode 100644
index 0000000..e43e570
--- /dev/null
+++ b/content/browser/cert_store.cc
@@ -0,0 +1,146 @@
+// 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 "content/browser/cert_store.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/stl_util-inl.h"
+#include "chrome/common/notification_service.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_view_host.h"
+
+template <typename T>
+struct MatchSecond {
+ explicit MatchSecond(const T& t) : value(t) {}
+
+ template<typename Pair>
+ bool operator()(const Pair& p) const {
+ return (value == p.second);
+ }
+ T value;
+};
+
+// static
+CertStore* CertStore::GetInstance() {
+ return Singleton<CertStore>::get();
+}
+
+CertStore::CertStore() : next_cert_id_(1) {
+ // We watch for RenderProcess termination, as this is how we clear
+ // certificates for now.
+ // TODO(jcampan): we should be listening to events such as resource cached/
+ // removed from cache, and remove the cert when we know it
+ // is not used anymore.
+
+ registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED,
+ NotificationService::AllSources());
+}
+
+CertStore::~CertStore() {
+}
+
+int CertStore::StoreCert(net::X509Certificate* cert, int process_id) {
+ DCHECK(cert);
+ base::AutoLock autoLock(cert_lock_);
+
+ int cert_id;
+
+ // Do we already know this cert?
+ ReverseCertMap::iterator cert_iter = cert_to_id_.find(cert);
+ if (cert_iter == cert_to_id_.end()) {
+ cert_id = next_cert_id_++;
+ // We use 0 as an invalid cert_id value. In the unlikely event that
+ // next_cert_id_ wraps around, we reset it to 1.
+ if (next_cert_id_ == 0)
+ next_cert_id_ = 1;
+ cert->AddRef();
+ id_to_cert_[cert_id] = cert;
+ cert_to_id_[cert] = cert_id;
+ } else {
+ cert_id = cert_iter->second;
+ }
+
+ // Let's update process_id_to_cert_id_.
+ if (std::find_if(process_id_to_cert_id_.lower_bound(process_id),
+ process_id_to_cert_id_.upper_bound(process_id),
+ MatchSecond<int>(cert_id)) ==
+ process_id_to_cert_id_.upper_bound(process_id)) {
+ process_id_to_cert_id_.insert(std::make_pair(process_id, cert_id));
+ }
+
+ // And cert_id_to_process_id_.
+ if (std::find_if(cert_id_to_process_id_.lower_bound(cert_id),
+ cert_id_to_process_id_.upper_bound(cert_id),
+ MatchSecond<int>(process_id)) ==
+ cert_id_to_process_id_.upper_bound(cert_id)) {
+ cert_id_to_process_id_.insert(std::make_pair(cert_id, process_id));
+ }
+
+ return cert_id;
+}
+
+bool CertStore::RetrieveCert(int cert_id,
+ scoped_refptr<net::X509Certificate>* cert) {
+ base::AutoLock autoLock(cert_lock_);
+
+ CertMap::iterator iter = id_to_cert_.find(cert_id);
+ if (iter == id_to_cert_.end())
+ return false;
+ if (cert)
+ *cert = iter->second;
+ return true;
+}
+
+void CertStore::RemoveCertInternal(int cert_id) {
+ CertMap::iterator cert_iter = id_to_cert_.find(cert_id);
+ DCHECK(cert_iter != id_to_cert_.end());
+
+ ReverseCertMap::iterator id_iter = cert_to_id_.find(cert_iter->second);
+ DCHECK(id_iter != cert_to_id_.end());
+ cert_to_id_.erase(id_iter);
+
+ cert_iter->second->Release();
+ id_to_cert_.erase(cert_iter);
+}
+
+void CertStore::RemoveCertsForRenderProcesHost(int process_id) {
+ base::AutoLock autoLock(cert_lock_);
+
+ // We iterate through all the cert ids for that process.
+ IDMap::iterator ids_iter;
+ for (ids_iter = process_id_to_cert_id_.lower_bound(process_id);
+ ids_iter != process_id_to_cert_id_.upper_bound(process_id);) {
+ int cert_id = ids_iter->second;
+ // Remove this process from cert_id_to_process_id_.
+ IDMap::iterator proc_iter =
+ std::find_if(cert_id_to_process_id_.lower_bound(cert_id),
+ cert_id_to_process_id_.upper_bound(cert_id),
+ MatchSecond<int>(process_id));
+ DCHECK(proc_iter != cert_id_to_process_id_.upper_bound(cert_id));
+ cert_id_to_process_id_.erase(proc_iter);
+
+ if (cert_id_to_process_id_.count(cert_id) == 0) {
+ // This cert is not referenced by any process, remove it from id_to_cert_
+ // and cert_to_id_.
+ RemoveCertInternal(cert_id);
+ }
+
+ // Erase the current item but keep the iterator valid.
+ process_id_to_cert_id_.erase(ids_iter++);
+ }
+}
+
+void CertStore::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::RENDERER_PROCESS_TERMINATED ||
+ type == NotificationType::RENDERER_PROCESS_CLOSED);
+ RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr();
+ DCHECK(rph);
+ RemoveCertsForRenderProcesHost(rph->id());
+}
diff --git a/content/browser/cert_store.h b/content/browser/cert_store.h
new file mode 100644
index 0000000..8022c9d
--- /dev/null
+++ b/content/browser/cert_store.h
@@ -0,0 +1,84 @@
+// 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 CONTENT_BROWSER_CERT_STORE_H_
+#define CONTENT_BROWSER_CERT_STORE_H_
+#pragma once
+
+#include <map>
+
+#include "base/singleton.h"
+#include "base/synchronization/lock.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "net/base/x509_certificate.h"
+
+// The purpose of the cert store is to provide an easy way to store/retrieve
+// X509Certificate objects. When stored, an X509Certificate object is
+// associated with a RenderProcessHost. If all the RenderProcessHosts
+// associated with the cert have exited, the cert is removed from the store.
+// This class is used by the SSLManager to keep track of the certs associated
+// to loaded resources.
+// It can be accessed from the UI and IO threads (it is thread-safe).
+// Note that the cert ids will overflow if we register more than 2^32 - 1 certs
+// in 1 browsing session (which is highly unlikely to happen).
+
+class CertStore : public NotificationObserver {
+ public:
+ // Returns the singleton instance of the CertStore.
+ static CertStore* GetInstance();
+
+ // Stores the specified cert and returns the id associated with it. The cert
+ // is associated to the specified RenderProcessHost.
+ // When all the RenderProcessHosts associated with a cert have exited, the
+ // cert is removed from the store.
+ // Note: ids starts at 1.
+ int StoreCert(net::X509Certificate* cert, int render_process_host_id);
+
+ // Tries to retrieve the previously stored cert associated with the specified
+ // |cert_id|. Returns whether the cert could be found, and, if |cert| is
+ // non-NULL, copies it in.
+ bool RetrieveCert(int cert_id, scoped_refptr<net::X509Certificate>* cert);
+
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ friend struct DefaultSingletonTraits<CertStore>;
+
+ CertStore();
+ ~CertStore();
+
+ // Remove the specified cert from id_to_cert_ and cert_to_id_.
+ // NOTE: the caller (RemoveCertsForRenderProcesHost) must hold cert_lock_.
+ void RemoveCertInternal(int cert_id);
+
+ // Removes all the certs associated with the specified process from the store.
+ void RemoveCertsForRenderProcesHost(int render_process_host_id);
+
+ typedef std::multimap<int, int> IDMap;
+ typedef std::map<int, scoped_refptr<net::X509Certificate> > CertMap;
+ typedef std::map<net::X509Certificate*, int, net::X509Certificate::LessThan>
+ ReverseCertMap;
+
+ NotificationRegistrar registrar_;
+
+ IDMap process_id_to_cert_id_;
+ IDMap cert_id_to_process_id_;
+
+ CertMap id_to_cert_;
+ ReverseCertMap cert_to_id_;
+
+ int next_cert_id_;
+
+ // This lock protects: process_to_ids_, id_to_processes_, id_to_cert_ and
+ // cert_to_id_.
+ base::Lock cert_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertStore);
+};
+
+#endif // CONTENT_BROWSER_CERT_STORE_H_
diff --git a/content/browser/certificate_manager_model.cc b/content/browser/certificate_manager_model.cc
new file mode 100644
index 0000000..c1660b6
--- /dev/null
+++ b/content/browser/certificate_manager_model.cc
@@ -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.
+
+#include "content/browser/certificate_manager_model.h"
+
+#include "base/i18n/time_formatting.h"
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/net/x509_certificate_model.h"
+#include "net/base/net_errors.h"
+#include "net/base/x509_certificate.h"
+
+CertificateManagerModel::CertificateManagerModel(Observer* observer)
+ : observer_(observer) {
+}
+
+CertificateManagerModel::~CertificateManagerModel() {
+}
+
+void CertificateManagerModel::Refresh() {
+ VLOG(1) << "refresh started";
+ cert_db_.ListCerts(&cert_list_);
+ observer_->CertificatesRefreshed();
+ VLOG(1) << "refresh finished";
+}
+
+void CertificateManagerModel::FilterAndBuildOrgGroupingMap(
+ net::CertType filter_type,
+ CertificateManagerModel::OrgGroupingMap* map) const {
+ for (net::CertificateList::const_iterator i = cert_list_.begin();
+ i != cert_list_.end(); ++i) {
+ net::X509Certificate* cert = i->get();
+ net::CertType type =
+ x509_certificate_model::GetType(cert->os_cert_handle());
+ if (type != filter_type)
+ continue;
+
+ std::string org;
+ if (!cert->subject().organization_names.empty())
+ org = cert->subject().organization_names[0];
+ if (org.empty())
+ org = cert->subject().GetDisplayName();
+
+ (*map)[org].push_back(cert);
+ }
+}
+
+string16 CertificateManagerModel::GetColumnText(
+ const net::X509Certificate& cert,
+ Column column) const {
+ string16 rv;
+ switch (column) {
+ case COL_SUBJECT_NAME:
+ rv = UTF8ToUTF16(
+ x509_certificate_model::GetCertNameOrNickname(cert.os_cert_handle()));
+ break;
+ case COL_CERTIFICATE_STORE:
+ rv = UTF8ToUTF16(
+ x509_certificate_model::GetTokenName(cert.os_cert_handle()));
+ break;
+ case COL_SERIAL_NUMBER:
+ rv = ASCIIToUTF16(
+ x509_certificate_model::GetSerialNumberHexified(
+ cert.os_cert_handle(), ""));
+ break;
+ case COL_EXPIRES_ON:
+ if (!cert.valid_expiry().is_null())
+ rv = base::TimeFormatShortDateNumeric(cert.valid_expiry());
+ break;
+ default:
+ NOTREACHED();
+ }
+ return rv;
+}
+
+int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module,
+ const std::string& data,
+ const string16& password) {
+ int result = cert_db_.ImportFromPKCS12(module, data, password);
+ if (result == net::OK)
+ Refresh();
+ return result;
+}
+
+bool CertificateManagerModel::ImportCACerts(
+ const net::CertificateList& certificates,
+ unsigned int trust_bits,
+ net::CertDatabase::ImportCertFailureList* not_imported) {
+ bool result = cert_db_.ImportCACerts(certificates, trust_bits, not_imported);
+ if (result && not_imported->size() != certificates.size())
+ Refresh();
+ return result;
+}
+
+bool CertificateManagerModel::ImportServerCert(
+ const net::CertificateList& certificates,
+ net::CertDatabase::ImportCertFailureList* not_imported) {
+ bool result = cert_db_.ImportServerCert(certificates, not_imported);
+ if (result && not_imported->size() != certificates.size())
+ Refresh();
+ return result;
+}
+
+bool CertificateManagerModel::SetCertTrust(const net::X509Certificate* cert,
+ net::CertType type,
+ unsigned int trust_bits) {
+ return cert_db_.SetCertTrust(cert, type, trust_bits);
+}
+
+bool CertificateManagerModel::Delete(net::X509Certificate* cert) {
+ bool result = cert_db_.DeleteCertAndKey(cert);
+ if (result)
+ Refresh();
+ return result;
+}
diff --git a/content/browser/certificate_manager_model.h b/content/browser/certificate_manager_model.h
new file mode 100644
index 0000000..f134a68
--- /dev/null
+++ b/content/browser/certificate_manager_model.h
@@ -0,0 +1,110 @@
+// 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 CONTENT_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
+#define CONTENT_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
+
+#include <map>
+#include <string>
+
+#include "base/ref_counted.h"
+#include "base/string16.h"
+#include "net/base/cert_database.h"
+
+// CertificateManagerModel provides the data to be displayed in the certificate
+// manager dialog, and processes changes from the view.
+class CertificateManagerModel {
+ public:
+ // Map from the subject organization name to the list of certs from that
+ // organization. If a cert does not have an organization name, the
+ // subject's CertPrincipal::GetDisplayName() value is used instead.
+ typedef std::map<std::string, net::CertificateList> OrgGroupingMap;
+
+ // Enumeration of the possible columns in the certificate manager tree view.
+ enum Column {
+ COL_SUBJECT_NAME,
+ COL_CERTIFICATE_STORE,
+ COL_SERIAL_NUMBER,
+ COL_EXPIRES_ON,
+ };
+
+ class Observer {
+ public:
+ // Called to notify the view that the certificate list has been refreshed.
+ // TODO(mattm): do a more granular updating strategy? Maybe retrieve new
+ // list of certs, diff against past list, and then notify of the changes?
+ virtual void CertificatesRefreshed() = 0;
+ };
+
+ explicit CertificateManagerModel(Observer* observer);
+ ~CertificateManagerModel();
+
+ // Accessor for read-only access to the underlying CertDatabase.
+ const net::CertDatabase& cert_db() const { return cert_db_; }
+
+ // Refresh the list of certs. Following this call, the observer
+ // CertificatesRefreshed method will be called so the view can call
+ // FilterAndBuildOrgGroupingMap as necessary to refresh its tree views.
+ void Refresh();
+
+ // Fill |map| with the certificates matching |filter_type|.
+ void FilterAndBuildOrgGroupingMap(net::CertType filter_type,
+ OrgGroupingMap* map) const;
+
+ // Get the data to be displayed in |column| for the given |cert|.
+ string16 GetColumnText(const net::X509Certificate& cert, Column column) const;
+
+ // Import certificates from PKCS #12 encoded |data|, using the given
+ // |password|. Returns a net error code on failure.
+ int ImportFromPKCS12(net::CryptoModule* module, const std::string& data,
+ const string16& password);
+
+ // Import CA certificates.
+ // Tries to import all the certificates given. The root will be trusted
+ // according to |trust_bits|. Any certificates that could not be imported
+ // will be listed in |not_imported|.
+ // |trust_bits| should be a bit field of TRUST_* values from CertDatabase, or
+ // UNTRUSTED.
+ // Returns false if there is an internal error, otherwise true is returned and
+ // |not_imported| should be checked for any certificates that were not
+ // imported.
+ bool ImportCACerts(const net::CertificateList& certificates,
+ unsigned int trust_bits,
+ net::CertDatabase::ImportCertFailureList* not_imported);
+
+ // Import server certificate. The first cert should be the server cert. Any
+ // additional certs should be intermediate/CA certs and will be imported but
+ // not given any trust.
+ // Any certificates that could not be imported will be listed in
+ // |not_imported|.
+ // Returns false if there is an internal error, otherwise true is returned and
+ // |not_imported| should be checked for any certificates that were not
+ // imported.
+ bool ImportServerCert(
+ const net::CertificateList& certificates,
+ net::CertDatabase::ImportCertFailureList* not_imported);
+
+ // Set trust values for certificate.
+ // |trust_bits| should be a bit field of TRUST_* values from CertDatabase, or
+ // UNTRUSTED.
+ // Returns true on success or false on failure.
+ bool SetCertTrust(const net::X509Certificate* cert,
+ net::CertType type,
+ unsigned int trust_bits);
+
+ // Delete the cert. Returns true on success. |cert| is still valid when this
+ // function returns.
+ bool Delete(net::X509Certificate* cert);
+
+ private:
+ net::CertDatabase cert_db_;
+ net::CertificateList cert_list_;
+
+ // The observer to notify when certificate list is refreshed.
+ Observer* observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertificateManagerModel);
+};
+
+#endif // CONTENT_BROWSER_CERTIFICATE_MANAGER_MODEL_H_
diff --git a/content/browser/certificate_viewer.cc b/content/browser/certificate_viewer.cc
new file mode 100644
index 0000000..3d92e7a
--- /dev/null
+++ b/content/browser/certificate_viewer.cc
@@ -0,0 +1,18 @@
+// 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 "content/browser/certificate_viewer.h"
+
+#include "content/browser/cert_store.h"
+
+void ShowCertificateViewerByID(gfx::NativeWindow parent, int cert_id) {
+ scoped_refptr<net::X509Certificate> cert;
+ CertStore::GetInstance()->RetrieveCert(cert_id, &cert);
+ if (!cert.get()) {
+ // The certificate was not found. Could be that the renderer crashed before
+ // we displayed the page info.
+ return;
+ }
+ ShowCertificateViewer(parent, cert);
+}
diff --git a/content/browser/certificate_viewer.h b/content/browser/certificate_viewer.h
new file mode 100644
index 0000000..8fa8befe
--- /dev/null
+++ b/content/browser/certificate_viewer.h
@@ -0,0 +1,25 @@
+// 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 CONTENT_BROWSER_CERTIFICATE_VIEWER_H_
+#define CONTENT_BROWSER_CERTIFICATE_VIEWER_H_
+#pragma once
+
+#include "ui/gfx/native_widget_types.h"
+
+namespace net {
+
+class X509Certificate;
+
+} // namespace net
+
+// Opens a certificate viewer under |parent| to display the certificate from
+// the |CertStore| with id |cert_id|.
+void ShowCertificateViewerByID(gfx::NativeWindow parent, int cert_id);
+
+// Opens a certificate viewer under |parent| to display |cert|.
+void ShowCertificateViewer(gfx::NativeWindow parent,
+ net::X509Certificate* cert);
+
+#endif // CONTENT_BROWSER_CERTIFICATE_VIEWER_H_
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
new file mode 100644
index 0000000..ded41be
--- /dev/null
+++ b/content/browser/child_process_launcher.cc
@@ -0,0 +1,348 @@
+// 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 "content/browser/child_process_launcher.h"
+
+#include <utility> // For std::pair.
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
+#include "chrome/common/chrome_descriptors.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/process_watcher.h"
+#include "chrome/common/result_codes.h"
+#include "content/browser/browser_thread.h"
+
+#if defined(OS_WIN)
+#include "base/file_path.h"
+#include "chrome/common/sandbox_policy.h"
+#elif defined(OS_LINUX)
+#include "base/singleton.h"
+#include "chrome/browser/crash_handler_host_linux.h"
+#include "chrome/browser/zygote_host_linux.h"
+#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "chrome/browser/mach_broker_mac.h"
+#endif
+
+#if defined(OS_POSIX)
+#include "base/global_descriptors_posix.h"
+#endif
+
+// Having the functionality of ChildProcessLauncher be in an internal
+// ref counted object allows us to automatically terminate the process when the
+// parent class destructs, while still holding on to state that we need.
+class ChildProcessLauncher::Context
+ : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {
+ public:
+ Context()
+ : client_(NULL),
+ client_thread_id_(BrowserThread::UI),
+ starting_(true)
+#if defined(OS_LINUX)
+ , zygote_(false)
+#endif
+ {
+ }
+
+ void Launch(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& environ,
+ int ipcfd,
+#endif
+ CommandLine* cmd_line,
+ Client* client) {
+ client_ = client;
+
+ CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
+
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &Context::LaunchInternal,
+#if defined(OS_WIN)
+ exposed_dir,
+#elif defined(OS_POSIX)
+ use_zygote,
+ environ,
+ ipcfd,
+#endif
+ cmd_line));
+ }
+
+ void ResetClient() {
+ // No need for locking as this function gets called on the same thread that
+ // client_ would be used.
+ CHECK(BrowserThread::CurrentlyOn(client_thread_id_));
+ client_ = NULL;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
+ friend class ChildProcessLauncher;
+
+ ~Context() {
+ Terminate();
+ }
+
+ void LaunchInternal(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& env,
+ int ipcfd,
+#endif
+ CommandLine* cmd_line) {
+ scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
+ base::ProcessHandle handle = base::kNullProcessHandle;
+#if defined(OS_WIN)
+ handle = sandbox::StartProcessWithAccess(cmd_line, exposed_dir);
+#elif defined(OS_POSIX)
+
+#if defined(OS_LINUX)
+ if (use_zygote) {
+ base::GlobalDescriptors::Mapping mapping;
+ mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd));
+ const int crash_signal_fd =
+ RendererCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket();
+ if (crash_signal_fd >= 0) {
+ mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal,
+ crash_signal_fd));
+ }
+ handle = ZygoteHost::GetInstance()->ForkRenderer(cmd_line->argv(),
+ mapping);
+ } else
+ // Fall through to the normal posix case below when we're not zygoting.
+#endif
+ {
+ base::file_handle_mapping_vector fds_to_map;
+ fds_to_map.push_back(std::make_pair(
+ ipcfd,
+ kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
+
+#if defined(OS_LINUX)
+ // On Linux, we need to add some extra file descriptors for crash handling
+ // and the sandbox.
+ bool is_renderer =
+ cmd_line->GetSwitchValueASCII(switches::kProcessType) ==
+ switches::kRendererProcess;
+ bool is_plugin =
+ cmd_line->GetSwitchValueASCII(switches::kProcessType) ==
+ switches::kPluginProcess;
+ bool is_gpu =
+ cmd_line->GetSwitchValueASCII(switches::kProcessType) ==
+ switches::kGpuProcess;
+
+ if (is_renderer || is_plugin || is_gpu) {
+ int crash_signal_fd;
+ if (is_renderer) {
+ crash_signal_fd = RendererCrashHandlerHostLinux::GetInstance()->
+ GetDeathSignalSocket();
+ } else if (is_plugin) {
+ crash_signal_fd = PluginCrashHandlerHostLinux::GetInstance()->
+ GetDeathSignalSocket();
+ } else {
+ crash_signal_fd = GpuCrashHandlerHostLinux::GetInstance()->
+ GetDeathSignalSocket();
+ }
+ if (crash_signal_fd >= 0) {
+ fds_to_map.push_back(std::make_pair(
+ crash_signal_fd,
+ kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor));
+ }
+ }
+ if (is_renderer) {
+ const int sandbox_fd =
+ RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
+ fds_to_map.push_back(std::make_pair(
+ sandbox_fd,
+ kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
+ }
+#endif // defined(OS_LINUX)
+
+ bool launched = false;
+#if defined(OS_MACOSX)
+ // It is possible for the child process to die immediately after
+ // launching. To prevent leaking MachBroker map entries in this case,
+ // lock around all of LaunchApp(). If the child dies, the death
+ // notification will be processed by the MachBroker after the call to
+ // AddPlaceholderForPid(), enabling proper cleanup.
+ { // begin scope for AutoLock
+ MachBroker* broker = MachBroker::GetInstance();
+ base::AutoLock lock(broker->GetLock());
+
+ // This call to |PrepareForFork()| will start the MachBroker listener
+ // thread, if it is not already running. Therefore the browser process
+ // will be listening for Mach IPC before LaunchApp() is called.
+ broker->PrepareForFork();
+#endif
+ // Actually launch the app.
+ launched = base::LaunchApp(cmd_line->argv(), env, fds_to_map,
+ /* wait= */false, &handle);
+#if defined(OS_MACOSX)
+ if (launched)
+ broker->AddPlaceholderForPid(handle);
+ } // end scope for AutoLock
+#endif
+ if (!launched)
+ handle = base::kNullProcessHandle;
+ }
+#endif // else defined(OS_POSIX)
+
+ BrowserThread::PostTask(
+ client_thread_id_, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &ChildProcessLauncher::Context::Notify,
+#if defined(OS_LINUX)
+ use_zygote,
+#endif
+ handle));
+ }
+
+ void Notify(
+#if defined(OS_LINUX)
+ bool zygote,
+#endif
+ base::ProcessHandle handle) {
+ starting_ = false;
+ process_.set_handle(handle);
+#if defined(OS_LINUX)
+ zygote_ = zygote;
+#endif
+ if (client_) {
+ client_->OnProcessLaunched();
+ } else {
+ Terminate();
+ }
+ }
+
+ void Terminate() {
+ if (!process_.handle())
+ return;
+
+ // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
+ // don't this on the UI/IO threads.
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ NewRunnableFunction(
+ &ChildProcessLauncher::Context::TerminateInternal,
+#if defined(OS_LINUX)
+ zygote_,
+#endif
+ process_.handle()));
+ process_.set_handle(base::kNullProcessHandle);
+ }
+
+ static void TerminateInternal(
+#if defined(OS_LINUX)
+ bool zygote,
+#endif
+ base::ProcessHandle handle) {
+ base::Process process(handle);
+ // Client has gone away, so just kill the process. Using exit code 0
+ // means that UMA won't treat this as a crash.
+ process.Terminate(ResultCodes::NORMAL_EXIT);
+ // On POSIX, we must additionally reap the child.
+#if defined(OS_POSIX)
+#if defined(OS_LINUX)
+ if (zygote) {
+ // If the renderer was created via a zygote, we have to proxy the reaping
+ // through the zygote process.
+ ZygoteHost::GetInstance()->EnsureProcessTerminated(handle);
+ } else
+#endif // OS_LINUX
+ {
+ ProcessWatcher::EnsureProcessTerminated(handle);
+ }
+#endif // OS_POSIX
+ process.Close();
+ }
+
+ Client* client_;
+ BrowserThread::ID client_thread_id_;
+ base::Process process_;
+ bool starting_;
+
+#if defined(OS_LINUX)
+ bool zygote_;
+#endif
+};
+
+
+ChildProcessLauncher::ChildProcessLauncher(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& environ,
+ int ipcfd,
+#endif
+ CommandLine* cmd_line,
+ Client* client) {
+ context_ = new Context();
+ context_->Launch(
+#if defined(OS_WIN)
+ exposed_dir,
+#elif defined(OS_POSIX)
+ use_zygote,
+ environ,
+ ipcfd,
+#endif
+ cmd_line,
+ client);
+}
+
+ChildProcessLauncher::~ChildProcessLauncher() {
+ context_->ResetClient();
+}
+
+bool ChildProcessLauncher::IsStarting() {
+ return context_->starting_;
+}
+
+base::ProcessHandle ChildProcessLauncher::GetHandle() {
+ DCHECK(!context_->starting_);
+ return context_->process_.handle();
+}
+
+base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
+ int* exit_code) {
+ base::TerminationStatus status;
+ base::ProcessHandle handle = context_->process_.handle();
+#if defined(OS_LINUX)
+ if (context_->zygote_) {
+ status = ZygoteHost::GetInstance()->GetTerminationStatus(handle, exit_code);
+ } else
+#endif
+ {
+ status = base::GetTerminationStatus(handle, exit_code);
+ }
+
+ // POSIX: If the process crashed, then the kernel closed the socket
+ // for it and so the child has already died by the time we get
+ // here. Since GetTerminationStatus called waitpid with WNOHANG,
+ // it'll reap the process. However, if GetTerminationStatus didn't
+ // reap the child (because it was still running), we'll need to
+ // Terminate via ProcessWatcher. So we can't close the handle here.
+ if (status != base::TERMINATION_STATUS_STILL_RUNNING)
+ context_->process_.Close();
+
+ return status;
+}
+
+void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
+ DCHECK(!context_->starting_);
+ context_->process_.SetProcessBackgrounded(background);
+}
diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
new file mode 100644
index 0000000..0174f15
--- /dev/null
+++ b/content/browser/child_process_launcher.h
@@ -0,0 +1,71 @@
+// 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 CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_H_
+#define CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/process_util.h"
+#include "base/ref_counted.h"
+
+class CommandLine;
+
+// Launches a process asynchronously and notifies the client of the process
+// handle when it's available. It's used to avoid blocking the calling thread
+// on the OS since often it can take > 100 ms to create the process.
+class ChildProcessLauncher {
+ public:
+ class Client {
+ public:
+ // Will be called on the thread that the ChildProcessLauncher was
+ // constructed on.
+ virtual void OnProcessLaunched() = 0;
+
+ protected:
+ virtual ~Client() {}
+ };
+
+ // Launches the process asynchronously, calling the client when the result is
+ // ready. Deleting this object before the process is created is safe, since
+ // the callback won't be called. If the process is still running by the time
+ // this object destructs, it will be terminated.
+ // Takes ownership of cmd_line.
+ ChildProcessLauncher(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& environ,
+ int ipcfd,
+#endif
+ CommandLine* cmd_line,
+ Client* client);
+ ~ChildProcessLauncher();
+
+ // True if the process is being launched and so the handle isn't available.
+ bool IsStarting();
+
+ // Getter for the process handle. Only call after the process has started.
+ base::ProcessHandle GetHandle();
+
+ // Call this when the child process exits to know what happened to
+ // it. |exit_code| is the exit code of the process if it exited
+ // (e.g. status from waitpid if on posix, from GetExitCodeProcess on
+ // Windows). |exit_code| may be NULL.
+ base::TerminationStatus GetChildTerminationStatus(int* exit_code);
+
+ // Changes whether the process runs in the background or not. Only call
+ // this after the process has started.
+ void SetProcessBackgrounded(bool background);
+
+ private:
+ class Context;
+
+ scoped_refptr<Context> context_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildProcessLauncher);
+};
+
+#endif // CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_H_
diff --git a/content/browser/child_process_security_policy.cc b/content/browser/child_process_security_policy.cc
new file mode 100644
index 0000000..b57f7d5
--- /dev/null
+++ b/content/browser/child_process_security_policy.cc
@@ -0,0 +1,413 @@
+// Copyright (c) 2011 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/child_process_security_policy.h"
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/platform_file.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "chrome/common/bindings_policy.h"
+#include "chrome/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request.h"
+
+static const int kReadFilePermissions =
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_EXCLUSIVE_READ |
+ base::PLATFORM_FILE_ASYNC;
+
+// The SecurityState class is used to maintain per-child process security state
+// information.
+class ChildProcessSecurityPolicy::SecurityState {
+ public:
+ SecurityState()
+ : enabled_bindings_(0),
+ can_read_raw_cookies_(false) { }
+ ~SecurityState() {
+ scheme_policy_.clear();
+ }
+
+ // Grant permission to request URLs with the specified scheme.
+ void GrantScheme(const std::string& scheme) {
+ scheme_policy_[scheme] = true;
+ }
+
+ // Revoke permission to request URLs with the specified scheme.
+ void RevokeScheme(const std::string& scheme) {
+ scheme_policy_[scheme] = false;
+ }
+
+ // Grant certain permissions to a file.
+ void GrantPermissionsForFile(const FilePath& file, int permissions) {
+ file_permissions_[file.StripTrailingSeparators()] |= permissions;
+ }
+
+ // Revokes all permissions granted to a file.
+ void RevokeAllPermissionsForFile(const FilePath& file) {
+ file_permissions_.erase(file.StripTrailingSeparators());
+ }
+
+ void GrantBindings(int bindings) {
+ enabled_bindings_ |= bindings;
+ }
+
+ void GrantReadRawCookies() {
+ can_read_raw_cookies_ = true;
+ }
+
+ void RevokeReadRawCookies() {
+ can_read_raw_cookies_ = false;
+ }
+
+ // Determine whether permission has been granted to request url.
+ // Schemes that have not been granted default to being denied.
+ bool CanRequestURL(const GURL& url) {
+ SchemeMap::const_iterator judgment(scheme_policy_.find(url.scheme()));
+
+ if (judgment == scheme_policy_.end())
+ return false; // Unmentioned schemes are disallowed.
+
+ return judgment->second;
+ }
+
+ // Determine if the certain permissions have been granted to a file.
+ bool HasPermissionsForFile(const FilePath& file, int permissions) {
+ FilePath current_path = file.StripTrailingSeparators();
+ FilePath last_path;
+ while (current_path != last_path) {
+ if (file_permissions_.find(current_path) != file_permissions_.end())
+ return (file_permissions_[current_path] & permissions) == permissions;
+ last_path = current_path;
+ current_path = current_path.DirName();
+ }
+
+ return false;
+ }
+
+ bool has_web_ui_bindings() const {
+ return BindingsPolicy::is_web_ui_enabled(enabled_bindings_);
+ }
+
+ bool has_extension_bindings() const {
+ return BindingsPolicy::is_extension_enabled(enabled_bindings_);
+ }
+
+ bool can_read_raw_cookies() const {
+ return can_read_raw_cookies_;
+ }
+
+ private:
+ typedef std::map<std::string, bool> SchemeMap;
+ typedef std::map<FilePath, int> FileMap; // bit-set of PlatformFileFlags
+
+ // Maps URL schemes to whether permission has been granted or revoked:
+ // |true| means the scheme has been granted.
+ // |false| means the scheme has been revoked.
+ // If a scheme is not present in the map, then it has never been granted
+ // or revoked.
+ SchemeMap scheme_policy_;
+
+ // The set of files the child process is permited to upload to the web.
+ FileMap file_permissions_;
+
+ int enabled_bindings_;
+
+ bool can_read_raw_cookies_;
+
+ DISALLOW_COPY_AND_ASSIGN(SecurityState);
+};
+
+ChildProcessSecurityPolicy::ChildProcessSecurityPolicy() {
+ // We know about these schemes and believe them to be safe.
+ RegisterWebSafeScheme(chrome::kHttpScheme);
+ RegisterWebSafeScheme(chrome::kHttpsScheme);
+ RegisterWebSafeScheme(chrome::kFtpScheme);
+ RegisterWebSafeScheme(chrome::kDataScheme);
+ RegisterWebSafeScheme("feed");
+ RegisterWebSafeScheme(chrome::kExtensionScheme);
+ RegisterWebSafeScheme(chrome::kBlobScheme);
+
+ // We know about the following psuedo schemes and treat them specially.
+ RegisterPseudoScheme(chrome::kAboutScheme);
+ RegisterPseudoScheme(chrome::kJavaScriptScheme);
+ RegisterPseudoScheme(chrome::kViewSourceScheme);
+}
+
+ChildProcessSecurityPolicy::~ChildProcessSecurityPolicy() {
+ web_safe_schemes_.clear();
+ pseudo_schemes_.clear();
+ STLDeleteContainerPairSecondPointers(security_state_.begin(),
+ security_state_.end());
+ security_state_.clear();
+}
+
+// static
+ChildProcessSecurityPolicy* ChildProcessSecurityPolicy::GetInstance() {
+ return Singleton<ChildProcessSecurityPolicy>::get();
+}
+
+void ChildProcessSecurityPolicy::Add(int child_id) {
+ base::AutoLock lock(lock_);
+ if (security_state_.count(child_id) != 0) {
+ NOTREACHED() << "Add child process at most once.";
+ return;
+ }
+
+ security_state_[child_id] = new SecurityState();
+}
+
+void ChildProcessSecurityPolicy::Remove(int child_id) {
+ base::AutoLock lock(lock_);
+ if (!security_state_.count(child_id))
+ return; // May be called multiple times.
+
+ delete security_state_[child_id];
+ security_state_.erase(child_id);
+}
+
+void ChildProcessSecurityPolicy::RegisterWebSafeScheme(
+ const std::string& scheme) {
+ base::AutoLock lock(lock_);
+ DCHECK(web_safe_schemes_.count(scheme) == 0) << "Add schemes at most once.";
+ DCHECK(pseudo_schemes_.count(scheme) == 0) << "Web-safe implies not psuedo.";
+
+ web_safe_schemes_.insert(scheme);
+}
+
+bool ChildProcessSecurityPolicy::IsWebSafeScheme(const std::string& scheme) {
+ base::AutoLock lock(lock_);
+
+ return (web_safe_schemes_.find(scheme) != web_safe_schemes_.end());
+}
+
+void ChildProcessSecurityPolicy::RegisterPseudoScheme(
+ const std::string& scheme) {
+ base::AutoLock lock(lock_);
+ DCHECK(pseudo_schemes_.count(scheme) == 0) << "Add schemes at most once.";
+ DCHECK(web_safe_schemes_.count(scheme) == 0) <<
+ "Psuedo implies not web-safe.";
+
+ pseudo_schemes_.insert(scheme);
+}
+
+bool ChildProcessSecurityPolicy::IsPseudoScheme(const std::string& scheme) {
+ base::AutoLock lock(lock_);
+
+ return (pseudo_schemes_.find(scheme) != pseudo_schemes_.end());
+}
+
+void ChildProcessSecurityPolicy::GrantRequestURL(
+ int child_id, const GURL& url) {
+
+ if (!url.is_valid())
+ return; // Can't grant the capability to request invalid URLs.
+
+ if (IsWebSafeScheme(url.scheme()))
+ return; // The scheme has already been whitelisted for every child process.
+
+ if (IsPseudoScheme(url.scheme())) {
+ // The view-source scheme is a special case of a pseudo-URL that eventually
+ // results in requesting its embedded URL.
+ if (url.SchemeIs(chrome::kViewSourceScheme)) {
+ // URLs with the view-source scheme typically look like:
+ // view-source:http://www.google.com/a
+ // In order to request these URLs, the child_id needs to be able to
+ // request the embedded URL.
+ GrantRequestURL(child_id, GURL(url.path()));
+ }
+
+ return; // Can't grant the capability to request pseudo schemes.
+ }
+
+ {
+ base::AutoLock lock(lock_);
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return;
+
+ // If the child process has been commanded to request a scheme, then we
+ // grant it the capability to request URLs of that scheme.
+ state->second->GrantScheme(url.scheme());
+ }
+}
+
+void ChildProcessSecurityPolicy::GrantReadFile(int child_id,
+ const FilePath& file) {
+ GrantPermissionsForFile(child_id, file, kReadFilePermissions);
+}
+
+void ChildProcessSecurityPolicy::GrantPermissionsForFile(
+ int child_id, const FilePath& file, int permissions) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return;
+
+ state->second->GrantPermissionsForFile(file, permissions);
+}
+
+void ChildProcessSecurityPolicy::RevokeAllPermissionsForFile(
+ int child_id, const FilePath& file) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return;
+
+ state->second->RevokeAllPermissionsForFile(file);
+}
+
+void ChildProcessSecurityPolicy::GrantScheme(int child_id,
+ const std::string& scheme) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return;
+
+ state->second->GrantScheme(scheme);
+}
+
+void ChildProcessSecurityPolicy::GrantWebUIBindings(int child_id) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return;
+
+ state->second->GrantBindings(BindingsPolicy::WEB_UI);
+
+ // Web UI bindings need the ability to request chrome: URLs.
+ state->second->GrantScheme(chrome::kChromeUIScheme);
+
+ // Web UI pages can contain links to file:// URLs.
+ state->second->GrantScheme(chrome::kFileScheme);
+}
+
+void ChildProcessSecurityPolicy::GrantExtensionBindings(int child_id) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return;
+
+ state->second->GrantBindings(BindingsPolicy::EXTENSION);
+}
+
+void ChildProcessSecurityPolicy::GrantReadRawCookies(int child_id) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return;
+
+ state->second->GrantReadRawCookies();
+}
+
+void ChildProcessSecurityPolicy::RevokeReadRawCookies(int child_id) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return;
+
+ state->second->RevokeReadRawCookies();
+}
+
+bool ChildProcessSecurityPolicy::CanRequestURL(
+ int child_id, const GURL& url) {
+ if (!url.is_valid())
+ return false; // Can't request invalid URLs.
+
+ if (IsWebSafeScheme(url.scheme()))
+ return true; // The scheme has been white-listed for every child process.
+
+ if (IsPseudoScheme(url.scheme())) {
+ // There are a number of special cases for pseudo schemes.
+
+ if (url.SchemeIs(chrome::kViewSourceScheme)) {
+ // A view-source URL is allowed if the child process is permitted to
+ // request the embedded URL. Careful to avoid pointless recursion.
+ GURL child_url(url.path());
+ if (child_url.SchemeIs(chrome::kViewSourceScheme) &&
+ url.SchemeIs(chrome::kViewSourceScheme))
+ return false;
+
+ return CanRequestURL(child_id, child_url);
+ }
+
+ if (LowerCaseEqualsASCII(url.spec(), chrome::kAboutBlankURL))
+ return true; // Every child process can request <about:blank>.
+
+ // URLs like <about:memory> and <about:crash> shouldn't be requestable by
+ // any child process. Also, this case covers <javascript:...>, which should
+ // be handled internally by the process and not kicked up to the browser.
+ return false;
+ }
+
+ if (!net::URLRequest::IsHandledURL(url))
+ return true; // This URL request is destined for ShellExecute.
+
+ {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return false;
+
+ // Otherwise, we consult the child process's security state to see if it is
+ // allowed to request the URL.
+ return state->second->CanRequestURL(url);
+ }
+}
+
+bool ChildProcessSecurityPolicy::CanReadFile(int child_id,
+ const FilePath& file) {
+ return HasPermissionsForFile(child_id, file, kReadFilePermissions);
+}
+
+bool ChildProcessSecurityPolicy::HasPermissionsForFile(
+ int child_id, const FilePath& file, int permissions) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return false;
+
+ return state->second->HasPermissionsForFile(file, permissions);
+}
+
+bool ChildProcessSecurityPolicy::HasWebUIBindings(int child_id) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return false;
+
+ return state->second->has_web_ui_bindings();
+}
+
+bool ChildProcessSecurityPolicy::HasExtensionBindings(int child_id) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return false;
+
+ return state->second->has_extension_bindings();
+}
+
+bool ChildProcessSecurityPolicy::CanReadRawCookies(int child_id) {
+ base::AutoLock lock(lock_);
+
+ SecurityStateMap::iterator state = security_state_.find(child_id);
+ if (state == security_state_.end())
+ return false;
+
+ return state->second->can_read_raw_cookies();
+}
diff --git a/content/browser/child_process_security_policy.h b/content/browser/child_process_security_policy.h
new file mode 100644
index 0000000..2f2df5e
--- /dev/null
+++ b/content/browser/child_process_security_policy.h
@@ -0,0 +1,164 @@
+// Copyright (c) 2011 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_CHILD_PROCESS_SECURITY_POLICY_H_
+#define CONTENT_BROWSER_CHILD_PROCESS_SECURITY_POLICY_H_
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/singleton.h"
+#include "base/synchronization/lock.h"
+
+class FilePath;
+class GURL;
+
+// The ChildProcessSecurityPolicy class is used to grant and revoke security
+// capabilities for child porcesses. For example, it restricts whether a child
+// process is permmitted to loaded file:// URLs based on whether the process
+// has ever been commanded to load file:// URLs by the browser.
+//
+// ChildProcessSecurityPolicy is a singleton that may be used on any thread.
+//
+class ChildProcessSecurityPolicy {
+ public:
+ // Object can only be created through GetInstance() so the constructor is
+ // private.
+ ~ChildProcessSecurityPolicy();
+
+ // There is one global ChildProcessSecurityPolicy object for the entire
+ // browser process. The object returned by this method may be accessed on
+ // any thread.
+ static ChildProcessSecurityPolicy* GetInstance();
+
+ // Web-safe schemes can be requested by any child process. Once a web-safe
+ // scheme has been registered, any child process can request URLs with
+ // that scheme. There is no mechanism for revoking web-safe schemes.
+ void RegisterWebSafeScheme(const std::string& scheme);
+
+ // Returns true iff |scheme| has been registered as a web-safe scheme.
+ bool IsWebSafeScheme(const std::string& scheme);
+
+ // Pseudo schemes are treated differently than other schemes because they
+ // cannot be requested like normal URLs. There is no mechanism for revoking
+ // pseudo schemes.
+ void RegisterPseudoScheme(const std::string& scheme);
+
+ // Returns true iff |scheme| has been registered as pseudo scheme.
+ bool IsPseudoScheme(const std::string& scheme);
+
+ // Upon creation, child processes should register themselves by calling this
+ // this method exactly once.
+ void Add(int child_id);
+
+ // Upon destruction, child processess should unregister themselves by caling
+ // this method exactly once.
+ void Remove(int child_id);
+
+ // Whenever the browser processes commands the child process to request a URL,
+ // it should call this method to grant the child process the capability to
+ // request the URL.
+ void GrantRequestURL(int child_id, const GURL& url);
+
+ // Whenever the user picks a file from a <input type="file"> element, the
+ // browser should call this function to grant the child process the capability
+ // to upload the file to the web.
+ void GrantReadFile(int child_id, const FilePath& file);
+
+ // Grants certain permissions to a file. |permissions| must be a bit-set of
+ // base::PlatformFileFlags.
+ void GrantPermissionsForFile(int child_id,
+ const FilePath& file,
+ int permissions);
+
+ // Revokes all permissions granted to the given file.
+ void RevokeAllPermissionsForFile(int child_id, const FilePath& file);
+
+ // Grants the child process the capability to access URLs of the provided
+ // scheme.
+ void GrantScheme(int child_id, const std::string& scheme);
+
+ // Grant the child process the ability to use Web UI Bindings.
+ void GrantWebUIBindings(int child_id);
+
+ // Grant the child process the ability to use extension Bindings.
+ void GrantExtensionBindings(int child_id);
+
+ // Grant the child process the ability to read raw cookies.
+ void GrantReadRawCookies(int child_id);
+
+ // Revoke read raw cookies permission.
+ void RevokeReadRawCookies(int child_id);
+
+ // Before servicing a child process's request for a URL, the browser should
+ // call this method to determine whether the process has the capability to
+ // request the URL.
+ bool CanRequestURL(int child_id, const GURL& url);
+
+ // Before servicing a child process's request to upload a file to the web, the
+ // browser should call this method to determine whether the process has the
+ // capability to upload the requested file.
+ bool CanReadFile(int child_id, const FilePath& file);
+
+ // Determines if certain permissions were granted for a file. |permissions|
+ // must be a bit-set of base::PlatformFileFlags.
+ bool HasPermissionsForFile(int child_id,
+ const FilePath& file,
+ int permissions);
+
+ // Returns true if the specified child_id has been granted WebUIBindings.
+ // The browser should check this property before assuming the child process is
+ // allowed to use WebUIBindings.
+ bool HasWebUIBindings(int child_id);
+
+ // Returns true if the specified child_id has been granted WebUIBindings.
+ // The browser should check this property before assuming the child process is
+ // allowed to use extension bindings.
+ bool HasExtensionBindings(int child_id);
+
+ // Returns true if the specified child_id has been granted ReadRawCookies.
+ bool CanReadRawCookies(int child_id);
+
+ private:
+ friend class ChildProcessSecurityPolicyInProcessBrowserTest;
+ FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyInProcessBrowserTest,
+ NoLeak);
+
+ class SecurityState;
+
+ typedef std::set<std::string> SchemeSet;
+ typedef std::map<int, SecurityState*> SecurityStateMap;
+
+ // Obtain an instance of ChildProcessSecurityPolicy via GetInstance().
+ ChildProcessSecurityPolicy();
+ friend struct DefaultSingletonTraits<ChildProcessSecurityPolicy>;
+
+ // You must acquire this lock before reading or writing any members of this
+ // class. You must not block while holding this lock.
+ base::Lock lock_;
+
+ // These schemes are white-listed for all child processes. This set is
+ // protected by |lock_|.
+ SchemeSet web_safe_schemes_;
+
+ // These schemes do not actually represent retrievable URLs. For example,
+ // the the URLs in the "about" scheme are aliases to other URLs. This set is
+ // protected by |lock_|.
+ SchemeSet pseudo_schemes_;
+
+ // This map holds a SecurityState for each child process. The key for the
+ // map is the ID of the ChildProcessHost. The SecurityState objects are
+ // owned by this object and are protected by |lock_|. References to them must
+ // not escape this class.
+ SecurityStateMap security_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildProcessSecurityPolicy);
+};
+
+#endif // CONTENT_BROWSER_CHILD_PROCESS_SECURITY_POLICY_H_
diff --git a/content/browser/child_process_security_policy_browsertest.cc b/content/browser/child_process_security_policy_browsertest.cc
new file mode 100644
index 0000000..f28001d
--- /dev/null
+++ b/content/browser/child_process_security_policy_browsertest.cc
@@ -0,0 +1,52 @@
+// 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 <string>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/process_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/result_codes.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/ui_test_utils.h"
+#include "content/browser/child_process_security_policy.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class ChildProcessSecurityPolicyInProcessBrowserTest
+ : public InProcessBrowserTest {
+ public:
+ virtual void SetUp() {
+ EXPECT_EQ(
+ ChildProcessSecurityPolicy::GetInstance()->security_state_.size(), 0U);
+ InProcessBrowserTest::SetUp();
+ }
+
+ virtual void TearDown() {
+ EXPECT_EQ(
+ ChildProcessSecurityPolicy::GetInstance()->security_state_.size(), 0U);
+ InProcessBrowserTest::TearDown();
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(ChildProcessSecurityPolicyInProcessBrowserTest, NoLeak) {
+ const FilePath kTestDir(FILE_PATH_LITERAL("google"));
+ const FilePath kTestFile(FILE_PATH_LITERAL("google.html"));
+ GURL url(ui_test_utils::GetTestUrl(kTestDir, kTestFile));
+
+ ui_test_utils::NavigateToURL(browser(), url);
+ EXPECT_EQ(
+ ChildProcessSecurityPolicy::GetInstance()->security_state_.size(), 1U);
+
+ TabContents* tab = browser()->GetTabContentsAt(0);
+ ASSERT_TRUE(tab != NULL);
+ base::KillProcess(tab->GetRenderProcessHost()->GetHandle(),
+ ResultCodes::KILLED, true);
+
+ tab->controller().Reload(true);
+ EXPECT_EQ(
+ ChildProcessSecurityPolicy::GetInstance()->security_state_.size(), 1U);
+}
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
new file mode 100644
index 0000000..92ba8d2
--- /dev/null
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -0,0 +1,333 @@
+// Copyright (c) 2011 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 <string>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/platform_file.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/child_process_security_policy.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_test_job.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class ChildProcessSecurityPolicyTest : public testing::Test {
+ protected:
+ // testing::Test
+ virtual void SetUp() {
+ // In the real world, "chrome:" is a handled scheme.
+ net::URLRequest::RegisterProtocolFactory(chrome::kChromeUIScheme,
+ &net::URLRequestTestJob::Factory);
+ }
+ virtual void TearDown() {
+ net::URLRequest::RegisterProtocolFactory(chrome::kChromeUIScheme, NULL);
+ }
+};
+
+static int kRendererID = 42;
+
+TEST_F(ChildProcessSecurityPolicyTest, IsWebSafeSchemeTest) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ EXPECT_TRUE(p->IsWebSafeScheme(chrome::kHttpScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(chrome::kHttpsScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(chrome::kFtpScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme(chrome::kDataScheme));
+ EXPECT_TRUE(p->IsWebSafeScheme("feed"));
+ EXPECT_TRUE(p->IsWebSafeScheme(chrome::kExtensionScheme));
+
+ EXPECT_FALSE(p->IsWebSafeScheme("registered-web-safe-scheme"));
+ p->RegisterWebSafeScheme("registered-web-safe-scheme");
+ EXPECT_TRUE(p->IsWebSafeScheme("registered-web-safe-scheme"));
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, IsPseudoSchemeTest) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ EXPECT_TRUE(p->IsPseudoScheme(chrome::kAboutScheme));
+ EXPECT_TRUE(p->IsPseudoScheme(chrome::kJavaScriptScheme));
+ EXPECT_TRUE(p->IsPseudoScheme(chrome::kViewSourceScheme));
+
+ EXPECT_FALSE(p->IsPseudoScheme("registered-psuedo-scheme"));
+ p->RegisterPseudoScheme("registered-psuedo-scheme");
+ EXPECT_TRUE(p->IsPseudoScheme("registered-psuedo-scheme"));
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, StandardSchemesTest) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ p->Add(kRendererID);
+
+ // Safe
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("http://www.google.com/")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("https://www.paypal.com/")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("ftp://ftp.gnu.org/")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("data:text/html,<b>Hi</b>")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID,
+ GURL("view-source:http://www.google.com/")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("chrome-extension://xy/z")));
+
+ // Dangerous
+ EXPECT_FALSE(p->CanRequestURL(kRendererID,
+ GURL("file:///etc/passwd")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID,
+ GURL("chrome://foo/bar")));
+
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, AboutTest) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ p->Add(kRendererID);
+
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("about:blank")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("about:BlAnK")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("aBouT:BlAnK")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("aBouT:blank")));
+
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:memory")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:crash")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:cache")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:hang")));
+
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("aBoUt:memory")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:CrASh")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("abOuT:cAChe")));
+
+ p->GrantRequestURL(kRendererID, GURL(chrome::kAboutMemoryURL));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL(chrome::kAboutMemoryURL)));
+
+ p->GrantRequestURL(kRendererID, GURL(chrome::kAboutCrashURL));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL(chrome::kAboutCrashURL)));
+
+ p->GrantRequestURL(kRendererID, GURL(chrome::kAboutCacheURL));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL(chrome::kAboutCacheURL)));
+
+ p->GrantRequestURL(kRendererID, GURL(chrome::kAboutHangURL));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL(chrome::kAboutHangURL)));
+
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, JavaScriptTest) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ p->Add(kRendererID);
+
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')")));
+ p->GrantRequestURL(kRendererID, GURL("javascript:alert('xss')"));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')")));
+
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, RegisterWebSafeSchemeTest) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ p->Add(kRendererID);
+
+ // Currently, "asdf" is destined for ShellExecute, so it is allowed.
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers")));
+
+ // Once we register a ProtocolFactory for "asdf", we default to deny.
+ net::URLRequest::RegisterProtocolFactory("asdf",
+ &net::URLRequestTestJob::Factory);
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("asdf:rockers")));
+
+ // We can allow new schemes by adding them to the whitelist.
+ p->RegisterWebSafeScheme("asdf");
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers")));
+
+ // Cleanup.
+ net::URLRequest::RegisterProtocolFactory("asdf", NULL);
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers")));
+
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, CanServiceCommandsTest) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ p->Add(kRendererID);
+
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
+ p->GrantRequestURL(kRendererID, GURL("file:///etc/passwd"));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
+
+ // We should forget our state if we repeat a renderer id.
+ p->Remove(kRendererID);
+ p->Add(kRendererID);
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, ViewSource) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ p->Add(kRendererID);
+
+ // View source is determined by the embedded scheme.
+ EXPECT_TRUE(p->CanRequestURL(kRendererID,
+ GURL("view-source:http://www.google.com/")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID,
+ GURL("view-source:file:///etc/passwd")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
+ EXPECT_FALSE(p->CanRequestURL(
+ kRendererID, GURL("view-source:view-source:http://www.google.com/")));
+
+ p->GrantRequestURL(kRendererID, GURL("view-source:file:///etc/passwd"));
+ // View source needs to be able to request the embedded scheme.
+ EXPECT_TRUE(p->CanRequestURL(kRendererID,
+ GURL("view-source:file:///etc/passwd")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
+
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, CanReadFiles) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ p->Add(kRendererID);
+
+ EXPECT_FALSE(p->CanReadFile(kRendererID,
+ FilePath(FILE_PATH_LITERAL("/etc/passwd"))));
+ p->GrantReadFile(kRendererID, FilePath(FILE_PATH_LITERAL("/etc/passwd")));
+ EXPECT_TRUE(p->CanReadFile(kRendererID,
+ FilePath(FILE_PATH_LITERAL("/etc/passwd"))));
+ EXPECT_FALSE(p->CanReadFile(kRendererID,
+ FilePath(FILE_PATH_LITERAL("/etc/shadow"))));
+
+ p->Remove(kRendererID);
+ p->Add(kRendererID);
+
+ EXPECT_FALSE(p->CanReadFile(kRendererID,
+ FilePath(FILE_PATH_LITERAL("/etc/passwd"))));
+ EXPECT_FALSE(p->CanReadFile(kRendererID,
+ FilePath(FILE_PATH_LITERAL("/etc/shadow"))));
+
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, FilePermissions) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ // Grant permissions for a file.
+ p->Add(kRendererID);
+ FilePath file = FilePath(FILE_PATH_LITERAL("/etc/passwd"));
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN));
+
+ p->GrantPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_TRUNCATE);
+ EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_TRUNCATE));
+ EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ));
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_CREATE));
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_TRUNCATE));
+ p->Remove(kRendererID);
+
+ // Grant permissions for the directory the file is in.
+ p->Add(kRendererID);
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN));
+ p->GrantPermissionsForFile(kRendererID, FilePath(FILE_PATH_LITERAL("/etc")),
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ);
+ EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN));
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE));
+ p->Remove(kRendererID);
+
+ // Grant permissions for the directory the file is in (with trailing '/').
+ p->Add(kRendererID);
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN));
+ p->GrantPermissionsForFile(kRendererID, FilePath(FILE_PATH_LITERAL("/etc/")),
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ);
+ EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN));
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE));
+
+ // Grant permissions for the file (should overwrite the permissions granted
+ // for the directory).
+ p->GrantPermissionsForFile(kRendererID, file, base::PLATFORM_FILE_TEMPORARY);
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN));
+ EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_TEMPORARY));
+
+ // Revoke all permissions for the file (it should inherit its permissions
+ // from the directory again).
+ p->RevokeAllPermissionsForFile(kRendererID, file);
+ EXPECT_TRUE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ));
+ EXPECT_FALSE(p->HasPermissionsForFile(kRendererID, file,
+ base::PLATFORM_FILE_TEMPORARY));
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, CanServiceWebUIBindings) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ GURL url("chrome://thumb/http://www.google.com/");
+
+ p->Add(kRendererID);
+
+ EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, url));
+ p->GrantWebUIBindings(kRendererID);
+ EXPECT_TRUE(p->HasWebUIBindings(kRendererID));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, url));
+
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, RemoveRace) {
+ ChildProcessSecurityPolicy* p = ChildProcessSecurityPolicy::GetInstance();
+
+ GURL url("file:///etc/passwd");
+ FilePath file(FILE_PATH_LITERAL("/etc/passwd"));
+
+ p->Add(kRendererID);
+
+ p->GrantRequestURL(kRendererID, url);
+ p->GrantReadFile(kRendererID, file);
+ p->GrantWebUIBindings(kRendererID);
+
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, url));
+ EXPECT_TRUE(p->CanReadFile(kRendererID, file));
+ EXPECT_TRUE(p->HasWebUIBindings(kRendererID));
+
+ p->Remove(kRendererID);
+
+ // Renderers are added and removed on the UI thread, but the policy can be
+ // queried on the IO thread. The ChildProcessSecurityPolicy needs to be
+ // prepared to answer policy questions about renderers who no longer exist.
+
+ // In this case, we default to secure behavior.
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, url));
+ EXPECT_FALSE(p->CanReadFile(kRendererID, file));
+ EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
+}
diff --git a/content/browser/chrome_blob_storage_context.cc b/content/browser/chrome_blob_storage_context.cc
new file mode 100644
index 0000000..245e860
--- /dev/null
+++ b/content/browser/chrome_blob_storage_context.cc
@@ -0,0 +1,22 @@
+// 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 "content/browser/chrome_blob_storage_context.h"
+
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "webkit/blob/blob_storage_controller.h"
+
+using webkit_blob::BlobStorageController;
+
+ChromeBlobStorageContext::ChromeBlobStorageContext() {
+}
+
+void ChromeBlobStorageContext::InitializeOnIOThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ controller_.reset(new BlobStorageController());
+}
+
+ChromeBlobStorageContext::~ChromeBlobStorageContext() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+}
diff --git a/content/browser/chrome_blob_storage_context.h b/content/browser/chrome_blob_storage_context.h
new file mode 100644
index 0000000..1c5fe55
--- /dev/null
+++ b/content/browser/chrome_blob_storage_context.h
@@ -0,0 +1,46 @@
+// 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 CONTENT_BROWSER_CHROME_BLOB_STORAGE_CONTEXT_H_
+#define CONTENT_BROWSER_CHROME_BLOB_STORAGE_CONTEXT_H_
+#pragma once
+
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/browser_thread.h"
+
+class GURL;
+
+namespace webkit_blob {
+class BlobStorageController;
+}
+
+// A context class that keeps track of BlobStorageController used by the chrome.
+// There is an instance associated with each Profile. There could be multiple
+// URLRequestContexts in the same profile that refers to the same instance.
+//
+// All methods, except the ctor, are expected to be called on
+// the IO thread (unless specifically called out in doc comments).
+class ChromeBlobStorageContext
+ : public base::RefCountedThreadSafe<ChromeBlobStorageContext,
+ BrowserThread::DeleteOnIOThread> {
+ public:
+ ChromeBlobStorageContext();
+
+ void InitializeOnIOThread();
+
+ webkit_blob::BlobStorageController* controller() const {
+ return controller_.get();
+ }
+
+ private:
+ friend class BrowserThread;
+ friend class DeleteTask<ChromeBlobStorageContext>;
+
+ virtual ~ChromeBlobStorageContext();
+
+ scoped_ptr<webkit_blob::BlobStorageController> controller_;
+};
+
+#endif // CONTENT_BROWSER_CHROME_BLOB_STORAGE_CONTEXT_H_
diff --git a/content/browser/cross_site_request_manager.cc b/content/browser/cross_site_request_manager.cc
new file mode 100644
index 0000000..38762bd
--- /dev/null
+++ b/content/browser/cross_site_request_manager.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2006-2008 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/cross_site_request_manager.h"
+
+#include "base/singleton.h"
+
+bool CrossSiteRequestManager::HasPendingCrossSiteRequest(int renderer_id,
+ int render_view_id) {
+ base::AutoLock lock(lock_);
+
+ std::pair<int, int> key(renderer_id, render_view_id);
+ return pending_cross_site_views_.find(key) !=
+ pending_cross_site_views_.end();
+}
+
+void CrossSiteRequestManager::SetHasPendingCrossSiteRequest(int renderer_id,
+ int render_view_id,
+ bool has_pending) {
+ base::AutoLock lock(lock_);
+
+ std::pair<int, int> key(renderer_id, render_view_id);
+ if (has_pending) {
+ pending_cross_site_views_.insert(key);
+ } else {
+ pending_cross_site_views_.erase(key);
+ }
+}
+
+CrossSiteRequestManager::CrossSiteRequestManager() {}
+
+CrossSiteRequestManager::~CrossSiteRequestManager() {}
+
+// static
+CrossSiteRequestManager* CrossSiteRequestManager::GetInstance() {
+ return Singleton<CrossSiteRequestManager>::get();
+}
diff --git a/content/browser/cross_site_request_manager.h b/content/browser/cross_site_request_manager.h
new file mode 100644
index 0000000..1274264
--- /dev/null
+++ b/content/browser/cross_site_request_manager.h
@@ -0,0 +1,60 @@
+// 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 CONTENT_BROWSER_CROSS_SITE_REQUEST_MANAGER_H_
+#define CONTENT_BROWSER_CROSS_SITE_REQUEST_MANAGER_H_
+#pragma once
+
+#include <set>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/synchronization/lock.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+// CrossSiteRequestManager is used to handle bookkeeping for cross-site
+// requests and responses between the UI and IO threads. Such requests involve
+// a transition from one RenderViewHost to another within TabContents, and
+// involve coordination with ResourceDispatcherHost.
+//
+// CrossSiteRequestManager is a singleton that may be used on any thread.
+//
+class CrossSiteRequestManager {
+ public:
+ // Returns the singleton instance.
+ static CrossSiteRequestManager* GetInstance();
+
+ // Returns whether the RenderViewHost specified by the given IDs currently
+ // has a pending cross-site request. If so, we will have to delay the
+ // response until the previous RenderViewHost runs its onunload handler.
+ // Called by ResourceDispatcherHost on the IO thread.
+ bool HasPendingCrossSiteRequest(int renderer_id, int render_view_id);
+
+ // Sets whether the RenderViewHost specified by the given IDs currently has a
+ // pending cross-site request. Called by RenderViewHost on the UI thread.
+ void SetHasPendingCrossSiteRequest(int renderer_id,
+ int render_view_id,
+ bool has_pending);
+
+ private:
+ friend struct DefaultSingletonTraits<CrossSiteRequestManager>;
+ typedef std::set<std::pair<int, int> > RenderViewSet;
+
+ CrossSiteRequestManager();
+ ~CrossSiteRequestManager();
+
+ // You must acquire this lock before reading or writing any members of this
+ // class. You must not block while holding this lock.
+ base::Lock lock_;
+
+ // Set of (render_process_host_id, render_view_id) pairs of all
+ // RenderViewHosts that have pending cross-site requests. Used to pass
+ // information about the RenderViewHosts between the UI and IO threads.
+ RenderViewSet pending_cross_site_views_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteRequestManager);
+};
+
+#endif // CONTENT_BROWSER_CROSS_SITE_REQUEST_MANAGER_H_
diff --git a/content/browser/disposition_utils.cc b/content/browser/disposition_utils.cc
new file mode 100644
index 0000000..fba7479
--- /dev/null
+++ b/content/browser/disposition_utils.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 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/disposition_utils.h"
+
+#include "build/build_config.h"
+
+namespace disposition_utils {
+
+WindowOpenDisposition DispositionFromClick(bool middle_button,
+ bool alt_key,
+ bool ctrl_key,
+ bool meta_key,
+ bool shift_key) {
+ // MacOS uses meta key (Command key) to spawn new tabs.
+#if defined(OS_MACOSX)
+ if (middle_button || meta_key)
+#else
+ if (middle_button || ctrl_key)
+#endif
+ return shift_key ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
+ if (shift_key)
+ return NEW_WINDOW;
+ if (alt_key)
+ return SAVE_TO_DISK;
+ return CURRENT_TAB;
+}
+
+}
diff --git a/content/browser/disposition_utils.h b/content/browser/disposition_utils.h
new file mode 100644
index 0000000..ba8c9e5
--- /dev/null
+++ b/content/browser/disposition_utils.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 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_DISPOSITION_UTILS_H_
+#define CONTENT_BROWSER_DISPOSITION_UTILS_H_
+#pragma once
+
+#include "webkit/glue/window_open_disposition.h"
+
+namespace disposition_utils {
+
+// Translates event flags from a click on a link into the user's desired
+// window disposition. For example, a middle click would mean to open
+// a background tab.
+WindowOpenDisposition DispositionFromClick(bool middle_button,
+ bool alt_key,
+ bool ctrl_key,
+ bool meta_key,
+ bool shift_key);
+
+}
+
+#endif // CONTENT_BROWSER_DISPOSITION_UTILS_H_
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 1629d8b..4a5a09a 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -20,6 +20,32 @@
'..',
],
'sources': [
+ 'browser/browser_child_process_host.cc',
+ 'browser/browser_child_process_host.h',
+ 'browser/browser_message_filter.cc',
+ 'browser/browser_message_filter.h',
+ 'browser/browser_thread.cc',
+ 'browser/browser_thread.h',
+ 'browser/browsing_instance.cc',
+ 'browser/browsing_instance.h',
+ 'browser/cancelable_request.cc',
+ 'browser/cancelable_request.h',
+ 'browser/cert_store.cc',
+ 'browser/cert_store.h',
+ 'browser/certificate_manager_model.cc',
+ 'browser/certificate_manager_model.h',
+ 'browser/certificate_viewer.cc',
+ 'browser/certificate_viewer.h',
+ 'browser/child_process_launcher.cc',
+ 'browser/child_process_launcher.h',
+ 'browser/child_process_security_policy.cc',
+ 'browser/child_process_security_policy.h',
+ 'browser/chrome_blob_storage_context.cc',
+ 'browser/chrome_blob_storage_context.h',
+ 'browser/cross_site_request_manager.cc',
+ 'browser/cross_site_request_manager.h',
+ 'browser/disposition_utils.cc',
+ 'browser/disposition_utils.h',
'browser/renderer_host/accelerated_surface_container_mac.cc',
'browser/renderer_host/accelerated_surface_container_mac.h',
'browser/renderer_host/accelerated_surface_container_manager_mac.cc',
@@ -144,6 +170,13 @@
'../build/linux/system.gyp:x11',
],
}],
+ ['OS!="linux"', {
+ 'sources!': [
+ # TODO(mattm): Cert manager stuff is really !USE_NSS.
+ 'browser/certificate_manager_model.cc',
+ 'browser/certificate_manager_model.h',
+ ],
+ }],
],
},
],