Move the rest of the core files in chrome\browser to content\browser.

TBR=avi
Review URL: http://codereview.chromium.org/6538111

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75711 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/content/DEPS b/content/DEPS
index bd39303..ffab3e2 100644
--- a/content/DEPS
+++ b/content/DEPS
@@ -13,6 +13,7 @@
   "+net",
   "+ppapi",
   "+printing",
+  "+sandbox",
   "+skia",
 
   # Don't allow inclusion of these other libs we shouldn't be calling directly.
diff --git a/content/browser/gpu.sb b/content/browser/gpu.sb
new file mode 100644
index 0000000..346bcfa
--- /dev/null
+++ b/content/browser/gpu.sb
@@ -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.
+;;
+
+; *** The contents of chrome/common/common.sb are implicitly included here. ***
+
+; The GPU process opens a shared memory file to communicate with the renderer.
+; This is backed by a file in /var/folders.
+; TODO(thakis): Let the browser allocated the pipe and hand the handles to
+;               renderer and GPU process and remove this: http://crbug.com/65344
+(allow file-read* file-write* (regex "^/(private/)?(tmp|var)(/|$)"))
+
+; Allow communication between the GPU process and the UI server.
+(allow mach-lookup (global-name "com.apple.tsm.uiserver"))
+
+(allow file-read-metadata (literal "/"))
diff --git a/content/browser/gpu_blacklist.cc b/content/browser/gpu_blacklist.cc
new file mode 100644
index 0000000..544a656
--- /dev/null
+++ b/content/browser/gpu_blacklist.cc
@@ -0,0 +1,577 @@
+// 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/gpu_blacklist.h"
+
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/sys_info.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "chrome/common/gpu_info.h"
+
+GpuBlacklist::VersionInfo::VersionInfo(const std::string& version_op,
+                                       const std::string& version_string,
+                                       const std::string& version_string2) {
+  op_ = StringToOp(version_op);
+  if (op_ == kUnknown || op_ == kAny)
+    return;
+  version_.reset(Version::GetVersionFromString(version_string));
+  if (version_.get() == NULL) {
+    op_ = kUnknown;
+    return;
+  }
+  if (op_ == kBetween) {
+    version2_.reset(Version::GetVersionFromString(version_string2));
+    if (version2_.get() == NULL)
+      op_ = kUnknown;
+  }
+}
+
+GpuBlacklist::VersionInfo::~VersionInfo() {
+}
+
+bool GpuBlacklist::VersionInfo::Contains(const Version& version) const {
+  if (op_ == kUnknown)
+    return false;
+  if (op_ == kAny)
+    return true;
+  if (op_ == kEQ) {
+    // Handles cases where 10.6 is considered as containing 10.6.*.
+    const std::vector<uint16>& components_reference = version_->components();
+    const std::vector<uint16>& components = version.components();
+    for (size_t i = 0; i < components_reference.size(); ++i) {
+      if (i >= components.size() && components_reference[i] != 0)
+        return false;
+      if (components[i] != components_reference[i])
+        return false;
+    }
+    return true;
+  }
+  int relation = version.CompareTo(*version_);
+  if (op_ == kEQ)
+    return (relation == 0);
+  else if (op_ == kLT)
+    return (relation < 0);
+  else if (op_ == kLE)
+    return (relation <= 0);
+  else if (op_ == kGT)
+    return (relation > 0);
+  else if (op_ == kGE)
+    return (relation >= 0);
+  // op_ == kBetween
+  if (relation < 0)
+    return false;
+  return version.CompareTo(*version2_) <= 0;
+}
+
+bool GpuBlacklist::VersionInfo::IsValid() const {
+  return op_ != kUnknown;
+}
+
+GpuBlacklist::VersionInfo::Op GpuBlacklist::VersionInfo::StringToOp(
+    const std::string& version_op) {
+  if (version_op == "=")
+    return kEQ;
+  else if (version_op == "<")
+    return kLT;
+  else if (version_op == "<=")
+    return kLE;
+  else if (version_op == ">")
+    return kGT;
+  else if (version_op == ">=")
+    return kGE;
+  else if (version_op == "any")
+    return kAny;
+  else if (version_op == "between")
+    return kBetween;
+  return kUnknown;
+}
+
+GpuBlacklist::OsInfo::OsInfo(const std::string& os,
+                             const std::string& version_op,
+                             const std::string& version_string,
+                             const std::string& version_string2) {
+  type_ = StringToOsType(os);
+  if (type_ != kOsUnknown) {
+    version_info_.reset(
+        new VersionInfo(version_op, version_string, version_string2));
+  }
+}
+
+GpuBlacklist::OsInfo::~OsInfo() {}
+
+bool GpuBlacklist::OsInfo::Contains(OsType type,
+                                    const Version& version) const {
+  if (!IsValid())
+    return false;
+  if (type_ != type && type_ != kOsAny)
+    return false;
+  return version_info_->Contains(version);
+}
+
+bool GpuBlacklist::OsInfo::IsValid() const {
+  return type_ != kOsUnknown && version_info_->IsValid();
+}
+
+GpuBlacklist::OsType GpuBlacklist::OsInfo::type() const {
+  return type_;
+}
+
+GpuBlacklist::OsType GpuBlacklist::OsInfo::StringToOsType(
+    const std::string& os) {
+  if (os == "win")
+    return kOsWin;
+  else if (os == "macosx")
+    return kOsMacosx;
+  else if (os == "linux")
+    return kOsLinux;
+  else if (os == "any")
+    return kOsAny;
+  return kOsUnknown;
+}
+
+GpuBlacklist::StringInfo::StringInfo(const std::string& string_op,
+                                     const std::string& string_value) {
+  op_ = StringToOp(string_op);
+  value_ = StringToLowerASCII(string_value);
+}
+
+bool GpuBlacklist::StringInfo::Contains(const std::string& value) const {
+  std::string my_value = StringToLowerASCII(value);
+  switch (op_) {
+    case kContains:
+      return strstr(my_value.c_str(), value_.c_str()) != NULL;
+    case kBeginWith:
+      return StartsWithASCII(my_value, value_, false);
+    case kEndWith:
+      return EndsWith(my_value, value_, false);
+    case kEQ:
+      return value_ == my_value;
+    default:
+      return false;
+  }
+}
+
+bool GpuBlacklist::StringInfo::IsValid() const {
+  return op_ != kUnknown;
+}
+
+GpuBlacklist::StringInfo::Op GpuBlacklist::StringInfo::StringToOp(
+    const std::string& string_op) {
+  if (string_op == "=")
+    return kEQ;
+  else if (string_op == "contains")
+    return kContains;
+  else if (string_op == "beginwith")
+    return kBeginWith;
+  else if (string_op == "endwith")
+    return kEndWith;
+  return kUnknown;
+}
+
+GpuBlacklist::GpuBlacklistEntry*
+GpuBlacklist::GpuBlacklistEntry::GetGpuBlacklistEntryFromValue(
+    DictionaryValue* value) {
+  if (value == NULL)
+    return NULL;
+
+  GpuBlacklistEntry* entry = new GpuBlacklistEntry();
+
+  std::string id;
+  if (!value->GetString("id", &id) || !entry->SetId(id)) {
+    delete entry;
+    return NULL;
+  }
+
+  DictionaryValue* os_value = NULL;
+  if (value->GetDictionary("os", &os_value)) {
+    std::string os_type;
+    std::string os_version_op = "any";
+    std::string os_version_string;
+    std::string os_version_string2;
+    os_value->GetString("type", &os_type);
+    DictionaryValue* os_version_value = NULL;
+    if (os_value->GetDictionary("version", &os_version_value)) {
+      os_version_value->GetString("op", &os_version_op);
+      os_version_value->GetString("number", &os_version_string);
+      os_version_value->GetString("number2", &os_version_string2);
+    }
+    if (!entry->SetOsInfo(os_type, os_version_op, os_version_string,
+                          os_version_string2)) {
+      delete entry;
+      return NULL;
+    }
+  }
+
+  std::string vendor_id;
+  if (value->GetString("vendor_id", &vendor_id)) {
+    if (!entry->SetVendorId(vendor_id)) {
+      delete entry;
+      return NULL;
+    }
+  }
+
+  std::string device_id;
+  if (value->GetString("device_id", &device_id)) {
+    if (!entry->SetDeviceId(device_id)) {
+      delete entry;
+      return NULL;
+    }
+  }
+
+  DictionaryValue* driver_vendor_value = NULL;
+  if (value->GetDictionary("driver_vendor", &driver_vendor_value)) {
+    std::string vendor_op;
+    std::string vendor_value;
+    driver_vendor_value->GetString("op", &vendor_op);
+    driver_vendor_value->GetString("value", &vendor_value);
+    if (!entry->SetDriverVendorInfo(vendor_op, vendor_value)) {
+      delete entry;
+      return NULL;
+    }
+  }
+
+  DictionaryValue* driver_version_value = NULL;
+  if (value->GetDictionary("driver_version", &driver_version_value)) {
+    std::string driver_version_op = "any";
+    std::string driver_version_string;
+    std::string driver_version_string2;
+    driver_version_value->GetString("op", &driver_version_op);
+    driver_version_value->GetString("number", &driver_version_string);
+    driver_version_value->GetString("number2", &driver_version_string2);
+    if (!entry->SetDriverVersionInfo(driver_version_op, driver_version_string,
+                                     driver_version_string2)) {
+      delete entry;
+      return NULL;
+    }
+  }
+
+  DictionaryValue* gl_renderer_value = NULL;
+  if (value->GetDictionary("gl_renderer", &gl_renderer_value)) {
+    std::string renderer_op;
+    std::string renderer_value;
+    gl_renderer_value->GetString("op", &renderer_op);
+    gl_renderer_value->GetString("value", &renderer_value);
+    if (!entry->SetGLRendererInfo(renderer_op, renderer_value)) {
+      delete entry;
+      return NULL;
+    }
+  }
+
+  ListValue* blacklist_value = NULL;
+  if (!value->GetList("blacklist", &blacklist_value)) {
+    delete entry;
+    return NULL;
+  }
+  std::vector<std::string> blacklist;
+  for (size_t i = 0; i < blacklist_value->GetSize(); ++i) {
+    std::string feature;
+    if (blacklist_value->GetString(i, &feature)) {
+      blacklist.push_back(feature);
+    } else {
+      delete entry;
+      return NULL;
+    }
+  }
+  if (!entry->SetBlacklistedFeatures(blacklist)) {
+    delete entry;
+    return NULL;
+  }
+
+  return entry;
+}
+
+GpuBlacklist::GpuBlacklistEntry::~GpuBlacklistEntry() {}
+
+GpuBlacklist::GpuBlacklistEntry::GpuBlacklistEntry()
+    : id_(0),
+      vendor_id_(0),
+      device_id_(0) {
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::SetId(
+    const std::string& id_string) {
+  int my_id;
+  if (base::HexStringToInt(id_string, &my_id) && my_id != 0) {
+    id_ = static_cast<uint32>(my_id);
+    return true;
+  }
+  return false;
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::SetOsInfo(
+    const std::string& os,
+    const std::string& version_op,
+    const std::string& version_string,
+    const std::string& version_string2) {
+  os_info_.reset(new OsInfo(os, version_op, version_string, version_string2));
+  return os_info_->IsValid();
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::SetVendorId(
+    const std::string& vendor_id_string) {
+  vendor_id_ = 0;
+  return base::HexStringToInt(vendor_id_string,
+                              reinterpret_cast<int*>(&vendor_id_));
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::SetDeviceId(
+    const std::string& device_id_string) {
+  device_id_ = 0;
+  return base::HexStringToInt(device_id_string,
+                              reinterpret_cast<int*>(&device_id_));
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::SetDriverVendorInfo(
+    const std::string& vendor_op,
+    const std::string& vendor_value) {
+  driver_vendor_info_.reset(
+      new StringInfo(vendor_op, vendor_value));
+  return driver_vendor_info_->IsValid();
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::SetDriverVersionInfo(
+    const std::string& version_op,
+    const std::string& version_string,
+    const std::string& version_string2) {
+  driver_version_info_.reset(
+      new VersionInfo(version_op, version_string, version_string2));
+  return driver_version_info_->IsValid();
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::SetGLRendererInfo(
+    const std::string& renderer_op,
+    const std::string& renderer_value) {
+  gl_renderer_info_.reset(
+      new StringInfo(renderer_op, renderer_value));
+  return gl_renderer_info_->IsValid();
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::SetBlacklistedFeatures(
+    const std::vector<std::string>& blacklisted_features) {
+  size_t size = blacklisted_features.size();
+  if (size == 0)
+    return false;
+  uint32 flags = 0;
+  for (size_t i = 0; i < size; ++i) {
+    GpuFeatureFlags::GpuFeatureType type =
+        GpuFeatureFlags::StringToGpuFeatureType(blacklisted_features[i]);
+    switch (type) {
+      case GpuFeatureFlags::kGpuFeatureAccelerated2dCanvas:
+      case GpuFeatureFlags::kGpuFeatureAcceleratedCompositing:
+      case GpuFeatureFlags::kGpuFeatureWebgl:
+      case GpuFeatureFlags::kGpuFeatureAll:
+        flags |= type;
+        break;
+      case GpuFeatureFlags::kGpuFeatureUnknown:
+        return false;
+    }
+  }
+  feature_flags_.reset(new GpuFeatureFlags());
+  feature_flags_->set_flags(flags);
+  return true;
+}
+
+bool GpuBlacklist::GpuBlacklistEntry::Contains(
+    OsType os_type, const Version& os_version, const GPUInfo& gpu_info) const {
+  DCHECK(os_type != kOsAny);
+  if (os_info_.get() != NULL && !os_info_->Contains(os_type, os_version))
+    return false;
+  if (vendor_id_ != 0 && vendor_id_ != gpu_info.vendor_id())
+    return false;
+  if (device_id_ != 0 && device_id_ != gpu_info.device_id())
+    return false;
+  if (driver_vendor_info_.get() != NULL &&
+      !driver_vendor_info_->Contains(gpu_info.driver_vendor()))
+    return false;
+  if (driver_version_info_.get() != NULL) {
+    scoped_ptr<Version> driver_version(
+        Version::GetVersionFromString(gpu_info.driver_version()));
+    if (driver_version.get() == NULL ||
+        !driver_version_info_->Contains(*driver_version))
+      return false;
+  }
+  if (gl_renderer_info_.get() != NULL &&
+      !gl_renderer_info_->Contains(gpu_info.gl_renderer()))
+    return false;
+  return true;
+}
+
+GpuBlacklist::OsType GpuBlacklist::GpuBlacklistEntry::GetOsType() const {
+  if (os_info_.get() == NULL)
+    return kOsAny;
+  return os_info_->type();
+}
+
+uint32 GpuBlacklist::GpuBlacklistEntry::id() const {
+  return id_;
+}
+
+GpuFeatureFlags GpuBlacklist::GpuBlacklistEntry::GetGpuFeatureFlags() const {
+  return *feature_flags_;
+}
+
+GpuBlacklist::GpuBlacklist()
+    : max_entry_id_(0) {
+}
+
+GpuBlacklist::~GpuBlacklist() {
+  Clear();
+}
+
+bool GpuBlacklist::LoadGpuBlacklist(const std::string& json_context,
+                                    bool current_os_only) {
+  std::vector<GpuBlacklistEntry*> entries;
+  scoped_ptr<Value> root;
+  root.reset(base::JSONReader::Read(json_context, false));
+  if (root.get() == NULL || !root->IsType(Value::TYPE_DICTIONARY))
+    return false;
+
+  DictionaryValue* root_dictionary = static_cast<DictionaryValue*>(root.get());
+  DCHECK(root_dictionary);
+  std::string version_string;
+  root_dictionary->GetString("version", &version_string);
+  version_.reset(Version::GetVersionFromString(version_string));
+  if (version_.get() == NULL)
+    return false;
+
+  ListValue* list = NULL;
+  root_dictionary->GetList("entries", &list);
+  if (list == NULL)
+    return false;
+
+  uint32 max_entry_id = 0;
+  for (size_t i = 0; i < list->GetSize(); ++i) {
+    DictionaryValue* list_item = NULL;
+    bool valid = list->GetDictionary(i, &list_item);
+    if (!valid)
+      break;
+    GpuBlacklistEntry* entry =
+        GpuBlacklistEntry::GetGpuBlacklistEntryFromValue(list_item);
+    if (entry == NULL)
+      break;
+    if (entry->id() > max_entry_id)
+      max_entry_id = entry->id();
+    entries.push_back(entry);
+  }
+
+  if (entries.size() < list->GetSize()) {
+    for (size_t i = 0; i < entries.size(); ++i)
+      delete entries[i];
+    return false;
+  }
+
+  Clear();
+  // Don't apply GPU blacklist for a non-registered OS.
+  OsType os_filter = GetOsType();
+  if (os_filter != kOsUnknown) {
+    for (size_t i = 0; i < entries.size(); ++i) {
+      OsType entry_os = entries[i]->GetOsType();
+      if (!current_os_only ||
+          entry_os == kOsAny || entry_os == os_filter)
+        blacklist_.push_back(entries[i]);
+      else
+        delete entries[i];
+    }
+  }
+  max_entry_id_ = max_entry_id;
+  return true;
+}
+
+GpuFeatureFlags GpuBlacklist::DetermineGpuFeatureFlags(
+    GpuBlacklist::OsType os,
+    Version* os_version,
+    const GPUInfo& gpu_info) {
+  active_entries_.clear();
+  GpuFeatureFlags flags;
+  // No need to go through blacklist entries if GPUInfo isn't available.
+  if (gpu_info.level() == GPUInfo::kUninitialized)
+    return flags;
+
+  if (os == kOsAny)
+    os = GetOsType();
+  scoped_ptr<Version> my_os_version;
+  if (os_version == NULL) {
+    std::string version_string;
+#if defined(OS_MACOSX)
+    // Seems like base::SysInfo::OperatingSystemVersion() returns the wrong
+    // version in MacOsx.
+    int32 version_major, version_minor, version_bugfix;
+    base::SysInfo::OperatingSystemVersionNumbers(
+        &version_major, &version_minor, &version_bugfix);
+    version_string = base::StringPrintf("%d.%d.%d",
+                                        version_major,
+                                        version_minor,
+                                        version_bugfix);
+#else
+    version_string = base::SysInfo::OperatingSystemVersion();
+    size_t pos = version_string.find_first_not_of("0123456789.");
+    if (pos != std::string::npos)
+      version_string = version_string.substr(0, pos);
+#endif
+    my_os_version.reset(Version::GetVersionFromString(version_string));
+    os_version = my_os_version.get();
+  }
+  DCHECK(os_version != NULL);
+
+  for (size_t i = 0; i < blacklist_.size(); ++i) {
+    if (blacklist_[i]->Contains(os, *os_version, gpu_info)) {
+      flags.Combine(blacklist_[i]->GetGpuFeatureFlags());
+      active_entries_.push_back(blacklist_[i]);
+    }
+  }
+  return flags;
+}
+
+void GpuBlacklist::GetGpuFeatureFlagEntries(
+    GpuFeatureFlags::GpuFeatureType feature,
+    std::vector<uint32>& entry_ids) const {
+  entry_ids.clear();
+  for (size_t i = 0; i < active_entries_.size(); ++i) {
+    if ((feature & active_entries_[i]->GetGpuFeatureFlags().flags()) != 0)
+      entry_ids.push_back(active_entries_[i]->id());
+  }
+}
+
+uint32 GpuBlacklist::max_entry_id() const {
+  return max_entry_id_;
+}
+
+bool GpuBlacklist::GetVersion(uint16* major, uint16* minor) const {
+  DCHECK(major && minor);
+  *major = 0;
+  *minor = 0;
+  if (version_.get() == NULL)
+    return false;
+  const std::vector<uint16>& components_reference = version_->components();
+  if (components_reference.size() != 2)
+    return false;
+  *major = components_reference[0];
+  *minor = components_reference[1];
+  return true;
+}
+
+GpuBlacklist::OsType GpuBlacklist::GetOsType() {
+#if defined(OS_WIN)
+  return kOsWin;
+#elif defined(OS_LINUX)
+  return kOsLinux;
+#elif defined(OS_MACOSX)
+  return kOsMacosx;
+#else
+  return kOsUnknown;
+#endif
+}
+
+void GpuBlacklist::Clear() {
+  for (size_t i = 0; i < blacklist_.size(); ++i)
+    delete blacklist_[i];
+  blacklist_.clear();
+  active_entries_.clear();
+}
+
diff --git a/content/browser/gpu_blacklist.h b/content/browser/gpu_blacklist.h
new file mode 100644
index 0000000..f732af9
--- /dev/null
+++ b/content/browser/gpu_blacklist.h
@@ -0,0 +1,227 @@
+// 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_GPU_BLACKLIST_H_
+#define CONTENT_BROWSER_GPU_BLACKLIST_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "chrome/common/gpu_feature_flags.h"
+
+class DictionaryValue;
+class GPUInfo;
+class Version;
+
+class GpuBlacklist {
+ public:
+  enum OsType {
+    kOsLinux,
+    kOsMacosx,
+    kOsWin,
+    kOsAny,
+    kOsUnknown
+  };
+
+  GpuBlacklist();
+  ~GpuBlacklist();
+
+  // Loads blacklist information from a json file.
+  // current_os_only==true indicates all blacklist entries that don't belong to
+  // the current OS are discarded; current_os_only==false should only be used
+  // for testing purpose.
+  // If failed, the current GpuBlacklist is un-touched.
+  bool LoadGpuBlacklist(const std::string& json_context,
+                        bool current_os_only);
+
+  // Collects system information and combines them with gpu_info and blacklist
+  // information to determine gpu feature flags.
+  // If os is kOsAny, use the current OS; if os_version is null, use the
+  // current OS version.
+  GpuFeatureFlags DetermineGpuFeatureFlags(OsType os,
+                                           Version* os_version,
+                                           const GPUInfo& gpu_info);
+
+  // Collects the entries that set the "feature" flag from the last
+  // DetermineGpuFeatureFlags() call.  This tells which entries are responsible
+  // for raising a certain flag, i.e, for blacklisting a certain feature.
+  // Examples of "feature":
+  //   kGpuFeatureAll - any of the supported features;
+  //   kGpuFeatureWebgl - a single feature;
+  //   kGpuFeatureWebgl | kGpuFeatureAcceleratedCompositing - two features.
+  void GetGpuFeatureFlagEntries(GpuFeatureFlags::GpuFeatureType feature,
+                                std::vector<uint32>& entry_ids) const;
+
+  // Return the largest entry id.  This is used for histogramming.
+  uint32 max_entry_id() const;
+
+  // Collects the version of the current blacklist.  Returns false and sets
+  // major and minor to 0 on failure.
+  bool GetVersion(uint16* major, uint16* monir) const;
+
+ private:
+  class VersionInfo {
+   public:
+    VersionInfo(const std::string& version_op,
+                const std::string& version_string,
+                const std::string& version_string2);
+    ~VersionInfo();
+
+    // Determines if a given version is included in the VersionInfo range.
+    bool Contains(const Version& version) const;
+
+    // Determines if the VersionInfo contains valid information.
+    bool IsValid() const;
+
+   private:
+    enum Op {
+      kBetween,  // <= * <=
+      kEQ,  // =
+      kLT,  // <
+      kLE,  // <=
+      kGT,  // >
+      kGE,  // >=
+      kAny,
+      kUnknown  // Indicates VersionInfo data is invalid.
+    };
+
+    // Maps string to Op; returns kUnknown if it's not a valid Op.
+    static Op StringToOp(const std::string& version_op);
+
+    Op op_;
+    scoped_ptr<Version> version_;
+    scoped_ptr<Version> version2_;
+  };
+
+  class OsInfo {
+   public:
+    OsInfo(const std::string& os,
+           const std::string& version_op,
+           const std::string& version_string,
+           const std::string& version_string2);
+    ~OsInfo();
+
+    // Determines if a given os/version is included in the OsInfo set.
+    bool Contains(OsType type, const Version& version) const;
+
+    // Determines if the VersionInfo contains valid information.
+    bool IsValid() const;
+
+    OsType type() const;
+
+    // Maps string to OsType; returns kOsUnknown if it's not a valid os.
+    static OsType StringToOsType(const std::string& os);
+
+   private:
+    OsType type_;
+    scoped_ptr<VersionInfo> version_info_;
+  };
+
+  class StringInfo {
+   public:
+    StringInfo(const std::string& string_op, const std::string& string_value);
+
+    // Determines if a given string is included in the StringInfo.
+    bool Contains(const std::string& value) const;
+
+    // Determines if the StringInfo contains valid information.
+    bool IsValid() const;
+
+   private:
+    enum Op {
+      kContains,
+      kBeginWith,
+      kEndWith,
+      kEQ,  // =
+      kUnknown  // Indicates StringInfo data is invalid.
+    };
+
+    // Maps string to Op; returns kUnknown if it's not a valid Op.
+    static Op StringToOp(const std::string& string_op);
+
+    Op op_;
+    std::string value_;
+  };
+
+  class GpuBlacklistEntry {
+   public:
+    // Constructs GpuBlacklistEntry from DictionaryValue loaded from json.
+    static GpuBlacklistEntry* GetGpuBlacklistEntryFromValue(
+        DictionaryValue* value);
+
+    // Determines if a given os/gc/driver is included in the Entry set.
+    bool Contains(OsType os_type,
+                  const Version& os_version,
+                  const GPUInfo& gpu_info) const;
+
+    // Returns the OsType.
+    OsType GetOsType() const;
+
+    // Returns the entry's unique id.  0 is reserved.
+    uint32 id() const;
+
+    // Returns the GpuFeatureFlags.
+    GpuFeatureFlags GetGpuFeatureFlags() const;
+
+    ~GpuBlacklistEntry();
+
+   private:
+    GpuBlacklistEntry();
+
+    bool SetId(const std::string& id_string);
+
+    bool SetOsInfo(const std::string& os,
+                   const std::string& version_op,
+                   const std::string& version_string,
+                   const std::string& version_string2);
+
+    bool SetVendorId(const std::string& vendor_id_string);
+
+    bool SetDeviceId(const std::string& device_id_string);
+
+    bool SetDriverVendorInfo(const std::string& vendor_op,
+                             const std::string& vendor_value);
+
+    bool SetDriverVersionInfo(const std::string& version_op,
+                              const std::string& version_string,
+                              const std::string& version_string2);
+
+    bool SetGLRendererInfo(const std::string& renderer_op,
+                           const std::string& renderer_value);
+
+    bool SetBlacklistedFeatures(
+        const std::vector<std::string>& blacklisted_features);
+
+    uint32 id_;
+    scoped_ptr<OsInfo> os_info_;
+    uint32 vendor_id_;
+    uint32 device_id_;
+    scoped_ptr<StringInfo> driver_vendor_info_;
+    scoped_ptr<VersionInfo> driver_version_info_;
+    scoped_ptr<StringInfo> gl_renderer_info_;
+    scoped_ptr<GpuFeatureFlags> feature_flags_;
+  };
+
+  // Gets the current OS type.
+  static OsType GetOsType();
+
+  void Clear();
+
+  scoped_ptr<Version> version_;
+  std::vector<GpuBlacklistEntry*> blacklist_;
+
+  // This records all the blacklist entries that are appliable to the current
+  // user machine.  It is updated everytime DetermineGpuFeatureFlags() is
+  // called and is used later by GetGpuFeatureFlagEntries().
+  std::vector<GpuBlacklistEntry*> active_entries_;
+
+  uint32 max_entry_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(GpuBlacklist);
+};
+
+#endif  // CONTENT_BROWSER_GPU_BLACKLIST_H_
diff --git a/content/browser/gpu_blacklist_unittest.cc b/content/browser/gpu_blacklist_unittest.cc
new file mode 100644
index 0000000..e2b496d
--- /dev/null
+++ b/content/browser/gpu_blacklist_unittest.cc
@@ -0,0 +1,176 @@
+// 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 <vector>
+
+#include "base/version.h"
+#include "chrome/common/gpu_info.h"
+#include "content/browser/gpu_blacklist.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(GpuBlacklistTest, BlacklistLogic) {
+  GPUInfo gpu_info;
+  gpu_info.SetVideoCardInfo(0x10de,  // Vendor ID
+                            0x0640);  // Device ID
+  gpu_info.SetDriverInfo("NVIDIA",  // Driver vendor
+                         "1.6.18");  // Driver Version
+  gpu_info.SetLevel(GPUInfo::kComplete);
+  scoped_ptr<Version> os_version(Version::GetVersionFromString("10.6.4"));
+
+  GpuBlacklist blacklist;
+
+  // Default blacklist settings: all feature are allowed.
+  GpuFeatureFlags flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsMacosx, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(), 0u);
+
+  // Empty list: all features are allowed.
+  const std::string empty_list_json =
+      "{\n"
+      "  \"name\": \"gpu blacklist\",\n"
+      "  \"version\": \"2.5\",\n"
+      "  \"entries\": [\n"
+      "  ]\n"
+      "}";
+  EXPECT_TRUE(blacklist.LoadGpuBlacklist(empty_list_json, false));
+  uint16 major, minor;
+  EXPECT_TRUE(blacklist.GetVersion(&major, &minor));
+  EXPECT_EQ(major, 2u);
+  EXPECT_EQ(minor, 5u);
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsMacosx, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(), 0u);
+
+  // Blacklist accelerated_compositing with exact setting.
+  const std::string exact_list_json =
+      "{\n"
+      "  \"name\": \"gpu blacklist\",\n"
+      "  \"version\": \"0.1\",\n"
+      "  \"entries\": [\n"
+      "    {\n"
+      "      \"id\": \"5\",\n"
+      "      \"os\": {\n"
+      "        \"type\": \"macosx\",\n"
+      "        \"version\": {\n"
+      "          \"op\": \"=\",\n"
+      "          \"number\": \"10.6.4\"\n"
+      "        }\n"
+      "      },\n"
+      "      \"vendor_id\": \"0x10de\",\n"
+      "      \"device_id\": \"0x0640\",\n"
+      "      \"driver_version\": {\n"
+      "        \"op\": \"=\",\n"
+      "        \"number\": \"1.6.18\"\n"
+      "      },\n"
+      "      \"blacklist\": [\n"
+      "        \"accelerated_compositing\"\n"
+      "      ]\n"
+      "    }\n"
+      "  ]\n"
+      "}";
+  EXPECT_TRUE(blacklist.LoadGpuBlacklist(exact_list_json, false));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsMacosx, os_version.get(), gpu_info);
+  EXPECT_EQ(
+      flags.flags(),
+      static_cast<uint32>(GpuFeatureFlags::kGpuFeatureAcceleratedCompositing));
+
+  // Invalid json input should not change the current blacklist settings.
+  const std::string invalid_json = "invalid";
+  EXPECT_FALSE(blacklist.LoadGpuBlacklist(invalid_json, false));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsMacosx, os_version.get(), gpu_info);
+  EXPECT_EQ(
+      flags.flags(),
+      static_cast<uint32>(GpuFeatureFlags::kGpuFeatureAcceleratedCompositing));
+  std::vector<uint32> entries;
+  blacklist.GetGpuFeatureFlagEntries(
+      GpuFeatureFlags::kGpuFeatureAcceleratedCompositing, entries);
+  EXPECT_EQ(entries.size(), 1u);
+  EXPECT_EQ(entries[0], 5u);
+  blacklist.GetGpuFeatureFlagEntries(
+      GpuFeatureFlags::kGpuFeatureAll, entries);
+  EXPECT_EQ(entries.size(), 1u);
+  EXPECT_EQ(entries[0], 5u);
+  EXPECT_EQ(blacklist.max_entry_id(), 5u);
+
+  // Blacklist a vendor on all OS.
+  const std::string vendor_json =
+      "{\n"
+      "  \"name\": \"gpu blacklist\",\n"
+      "  \"version\": \"0.1\",\n"
+      "  \"entries\": [\n"
+      "    {\n"
+      "      \"id\": \"1\",\n"
+      "      \"vendor_id\": \"0x10de\",\n"
+      "      \"blacklist\": [\n"
+      "        \"webgl\"\n"
+      "      ]\n"
+      "    }\n"
+      "  ]\n"
+      "}";
+  // Blacklist entries won't be filtered to the current OS only upon loading.
+  EXPECT_TRUE(blacklist.LoadGpuBlacklist(vendor_json, false));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsMacosx, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(),
+            static_cast<uint32>(GpuFeatureFlags::kGpuFeatureWebgl));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsWin, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(),
+            static_cast<uint32>(GpuFeatureFlags::kGpuFeatureWebgl));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsLinux, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(),
+            static_cast<uint32>(GpuFeatureFlags::kGpuFeatureWebgl));
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX)
+  // Blacklist entries will be filtered to the current OS only upon loading.
+  EXPECT_TRUE(blacklist.LoadGpuBlacklist(vendor_json, true));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsMacosx, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(),
+            static_cast<uint32>(GpuFeatureFlags::kGpuFeatureWebgl));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsWin, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(),
+            static_cast<uint32>(GpuFeatureFlags::kGpuFeatureWebgl));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsLinux, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(),
+            static_cast<uint32>(GpuFeatureFlags::kGpuFeatureWebgl));
+#endif
+
+
+  // Blacklist a vendor on Linux only.
+  const std::string vendor_linux_json =
+      "{\n"
+      "  \"name\": \"gpu blacklist\",\n"
+      "  \"version\": \"0.1\",\n"
+      "  \"entries\": [\n"
+      "    {\n"
+      "      \"id\": \"1\",\n"
+      "      \"os\": {\n"
+      "        \"type\": \"linux\"\n"
+      "      },\n"
+      "      \"vendor_id\": \"0x10de\",\n"
+      "      \"blacklist\": [\n"
+      "        \"accelerated_2d_canvas\"\n"
+      "      ]\n"
+      "    }\n"
+      "  ]\n"
+      "}";
+  EXPECT_TRUE(blacklist.LoadGpuBlacklist(vendor_linux_json, false));
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsMacosx, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(), 0u);
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsWin, os_version.get(), gpu_info);
+  EXPECT_EQ(flags.flags(), 0u);
+  flags = blacklist.DetermineGpuFeatureFlags(
+      GpuBlacklist::kOsLinux, os_version.get(), gpu_info);
+  EXPECT_EQ(
+      flags.flags(),
+      static_cast<uint32>(GpuFeatureFlags::kGpuFeatureAccelerated2dCanvas));
+}
+
diff --git a/content/browser/gpu_process_host.cc b/content/browser/gpu_process_host.cc
new file mode 100644
index 0000000..4ef366af
--- /dev/null
+++ b/content/browser/gpu_process_host.cc
@@ -0,0 +1,287 @@
+// 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/gpu_process_host.h"
+
+#include "app/app_switches.h"
+#include "base/metrics/histogram.h"
+#include "base/ref_counted.h"
+#include "base/string_piece.h"
+#include "base/threading/thread.h"
+#include "chrome/browser/gpu_process_host_ui_shim.h"
+#include "chrome/browser/tab_contents/render_view_host_delegate_helper.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/gpu_feature_flags.h"
+#include "chrome/common/gpu_info.h"
+#include "chrome/common/gpu_messages.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/gpu/gpu_thread.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/renderer_host/render_widget_host.h"
+#include "content/browser/renderer_host/render_widget_host_view.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_switches.h"
+#include "media/base/media_switches.h"
+
+#if defined(OS_LINUX)
+#include "ui/gfx/gtk_native_view_id_manager.h"
+#endif  // defined(OS_LINUX)
+
+namespace {
+
+enum GPUProcessLifetimeEvent {
+  LAUNCHED,
+  DIED_FIRST_TIME,
+  DIED_SECOND_TIME,
+  DIED_THIRD_TIME,
+  DIED_FOURTH_TIME,
+  GPU_PROCESS_LIFETIME_EVENT_MAX
+  };
+
+class RouteOnUIThreadTask : public Task {
+ public:
+  RouteOnUIThreadTask(int host_id, const IPC::Message& msg)
+      : host_id_(host_id),
+        msg_(msg) {
+  }
+
+ private:
+  virtual void Run() {
+    GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(host_id_);
+    if (ui_shim)
+      ui_shim->OnMessageReceived(msg_);
+  }
+
+  int host_id_;
+  IPC::Message msg_;
+};
+
+// A global map from GPU process host ID to GpuProcessHost.
+static IDMap<GpuProcessHost> g_hosts_by_id;
+
+// Number of times the gpu process has crashed in the current browser session.
+static int g_gpu_crash_count = 0;
+
+// Maximum number of times the gpu process is allowed to crash in a session.
+// Once this limit is reached, any request to launch the gpu process will fail.
+static const int kGpuMaxCrashCount = 3;
+
+}  // anonymous namespace
+
+class GpuMainThread : public base::Thread {
+ public:
+  explicit GpuMainThread(const std::string& channel_id)
+      : base::Thread("CrGpuMain"),
+        channel_id_(channel_id) {
+  }
+
+  ~GpuMainThread() {
+    Stop();
+  }
+
+ protected:
+  virtual void Init() {
+    // Must be created on GPU thread.
+    gpu_thread_.reset(new GpuThread(channel_id_));
+    gpu_thread_->Init(base::Time::Now());
+  }
+
+  virtual void CleanUp() {
+    // Must be destroyed on GPU thread.
+    gpu_thread_.reset();
+  }
+
+ private:
+  scoped_ptr<GpuThread> gpu_thread_;
+  std::string channel_id_;
+  DISALLOW_COPY_AND_ASSIGN(GpuMainThread);
+};
+
+// static
+GpuProcessHost* GpuProcessHost::Create(int host_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  GpuProcessHost* host = new GpuProcessHost(host_id);
+  if (!host->Init()) {
+    delete host;
+    return NULL;
+  }
+
+  return host;
+}
+
+// static
+GpuProcessHost* GpuProcessHost::FromID(int host_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (host_id == 0)
+    return NULL;
+
+  return g_hosts_by_id.Lookup(host_id);
+}
+
+GpuProcessHost::GpuProcessHost(int host_id)
+    : BrowserChildProcessHost(GPU_PROCESS, NULL),
+      host_id_(host_id) {
+  g_hosts_by_id.AddWithID(this, host_id_);
+}
+
+GpuProcessHost::~GpuProcessHost() {
+
+  DCHECK(CalledOnValidThread());
+
+  g_hosts_by_id.Remove(host_id_);
+
+  BrowserThread::PostTask(BrowserThread::UI,
+                          FROM_HERE,
+                          NewRunnableFunction(GpuProcessHostUIShim::Destroy,
+                                              host_id_));
+}
+
+bool GpuProcessHost::Init() {
+  if (!CreateChannel())
+    return false;
+
+  if (!CanLaunchGpuProcess())
+    return false;
+
+  if (!LaunchGpuProcess())
+    return false;
+
+  return Send(new GpuMsg_Initialize());
+}
+
+void GpuProcessHost::RouteOnUIThread(const IPC::Message& message) {
+  BrowserThread::PostTask(BrowserThread::UI,
+                          FROM_HERE,
+                          new RouteOnUIThreadTask(host_id_, message));
+}
+
+bool GpuProcessHost::Send(IPC::Message* msg) {
+  DCHECK(CalledOnValidThread());
+  return BrowserChildProcessHost::Send(msg);
+}
+
+bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) {
+  DCHECK(CalledOnValidThread());
+  RouteOnUIThread(message);
+  return true;
+}
+
+bool GpuProcessHost::CanShutdown() {
+  return true;
+}
+
+namespace {
+
+void SendOutstandingRepliesDispatcher(int host_id) {
+  GpuProcessHostUIShim *ui_shim = GpuProcessHostUIShim::FromID(host_id);
+  DCHECK(ui_shim);
+  ui_shim->SendOutstandingReplies();
+}
+
+void SendOutstandingReplies(int host_id) {
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      NewRunnableFunction(&SendOutstandingRepliesDispatcher, host_id));
+}
+
+}  // namespace
+
+void GpuProcessHost::OnChildDied() {
+  SendOutstandingReplies(host_id_);
+  // Located in OnChildDied because OnProcessCrashed suffers from a race
+  // condition on Linux.
+  UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents",
+                            DIED_FIRST_TIME + g_gpu_crash_count,
+                            GPU_PROCESS_LIFETIME_EVENT_MAX);
+  BrowserChildProcessHost::OnChildDied();
+}
+
+void GpuProcessHost::OnProcessCrashed(int exit_code) {
+  SendOutstandingReplies(host_id_);
+  if (++g_gpu_crash_count >= kGpuMaxCrashCount) {
+    // The gpu process is too unstable to use. Disable it for current session.
+    RenderViewHostDelegateHelper::set_gpu_enabled(false);
+  }
+  BrowserChildProcessHost::OnProcessCrashed(exit_code);
+}
+
+bool GpuProcessHost::CanLaunchGpuProcess() const {
+  return RenderViewHostDelegateHelper::gpu_enabled();
+}
+
+bool GpuProcessHost::LaunchGpuProcess() {
+  if (g_gpu_crash_count >= kGpuMaxCrashCount)
+    return false;
+
+  const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+
+  // If the single-process switch is present, just launch the GPU service in a
+  // new thread in the browser process.
+  if (browser_command_line.HasSwitch(switches::kSingleProcess)) {
+    GpuMainThread* thread = new GpuMainThread(channel_id());
+
+    base::Thread::Options options;
+#if defined(OS_LINUX)
+    options.message_loop_type = MessageLoop::TYPE_IO;
+#else
+    options.message_loop_type = MessageLoop::TYPE_UI;
+#endif
+
+    if (!thread->StartWithOptions(options))
+      return false;
+
+    return true;
+  }
+
+  CommandLine::StringType gpu_launcher =
+      browser_command_line.GetSwitchValueNative(switches::kGpuLauncher);
+
+  FilePath exe_path = ChildProcessHost::GetChildPath(gpu_launcher.empty());
+  if (exe_path.empty())
+    return false;
+
+  CommandLine* cmd_line = new CommandLine(exe_path);
+  cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess);
+  cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id());
+
+  SetCrashReporterCommandLine(cmd_line);
+
+  // Propagate relevant command line switches.
+  static const char* const kSwitchNames[] = {
+    switches::kUseGL,
+    switches::kDisableGpuVsync,
+    switches::kDisableGpuWatchdog,
+    switches::kDisableLogging,
+    switches::kEnableAcceleratedDecoding,
+    switches::kEnableLogging,
+#if defined(OS_MACOSX)
+    switches::kEnableSandboxLogging,
+#endif
+    switches::kGpuStartupDialog,
+    switches::kLoggingLevel,
+    switches::kNoGpuSandbox,
+    switches::kNoSandbox,
+  };
+  cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
+                             arraysize(kSwitchNames));
+
+  // If specified, prepend a launcher program to the command line.
+  if (!gpu_launcher.empty())
+    cmd_line->PrependWrapper(gpu_launcher);
+
+  Launch(
+#if defined(OS_WIN)
+      FilePath(),
+#elif defined(OS_POSIX)
+      false,  // Never use the zygote (GPU plugin can't be sandboxed).
+      base::environment_vector(),
+#endif
+      cmd_line);
+
+  UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents",
+                            LAUNCHED, GPU_PROCESS_LIFETIME_EVENT_MAX);
+  return true;
+}
diff --git a/content/browser/gpu_process_host.h b/content/browser/gpu_process_host.h
new file mode 100644
index 0000000..9881bee
--- /dev/null
+++ b/content/browser/gpu_process_host.h
@@ -0,0 +1,54 @@
+// 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_GPU_PROCESS_HOST_H_
+#define CONTENT_BROWSER_GPU_PROCESS_HOST_H_
+#pragma once
+
+#include "base/threading/non_thread_safe.h"
+#include "content/browser/browser_child_process_host.h"
+
+namespace IPC {
+class Message;
+}
+
+class GpuProcessHost : public BrowserChildProcessHost,
+                       public base::NonThreadSafe {
+ public:
+
+  // Create a GpuProcessHost with the given ID. The object can be found using
+  // FromID with the same id.
+  static GpuProcessHost* Create(int host_id);
+
+  // Get the GPU process host for the GPU process with the given ID. Returns
+  // null if the process no longer exists.
+  static GpuProcessHost* FromID(int host_id);
+
+  virtual bool Send(IPC::Message* msg);
+
+  // IPC::Channel::Listener implementation.
+  virtual bool OnMessageReceived(const IPC::Message& message);
+
+ private:
+  explicit GpuProcessHost(int host_id);
+  virtual ~GpuProcessHost();
+  bool Init();
+
+  // Post an IPC message to the UI shim's message handler on the UI thread.
+  void RouteOnUIThread(const IPC::Message& message);
+
+  virtual bool CanShutdown();
+  virtual void OnChildDied();
+  virtual void OnProcessCrashed(int exit_code);
+
+  bool CanLaunchGpuProcess() const;
+  bool LaunchGpuProcess();
+
+  // The serial number of the GpuProcessHost / GpuProcessHostUIShim pair.
+  int host_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(GpuProcessHost);
+};
+
+#endif  // CONTENT_BROWSER_GPU_PROCESS_HOST_H_
diff --git a/content/browser/host_zoom_map.cc b/content/browser/host_zoom_map.cc
new file mode 100644
index 0000000..0581036
--- /dev/null
+++ b/content/browser/host_zoom_map.cc
@@ -0,0 +1,260 @@
+// 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 <cmath>
+
+#include "content/browser/host_zoom_map.h"
+
+#include "base/string_piece.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/scoped_pref_update.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_util.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+
+using WebKit::WebView;
+
+HostZoomMap::HostZoomMap(Profile* profile)
+    : profile_(profile),
+      updating_preferences_(false) {
+  Load();
+  default_zoom_level_ =
+      profile_->GetPrefs()->GetDouble(prefs::kDefaultZoomLevel);
+  registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
+                 Source<Profile>(profile));
+  // Don't observe pref changes (e.g. from sync) in Incognito; once we create
+  // the incognito window it should have no further connection to the main
+  // profile/prefs.
+  if (!profile_->IsOffTheRecord()) {
+    pref_change_registrar_.Init(profile_->GetPrefs());
+    pref_change_registrar_.Add(prefs::kPerHostZoomLevels, this);
+    pref_change_registrar_.Add(prefs::kDefaultZoomLevel, this);
+  }
+
+  registrar_.Add(
+      this, NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
+      NotificationService::AllSources());
+}
+
+void HostZoomMap::Load() {
+  if (!profile_)
+    return;
+
+  base::AutoLock auto_lock(lock_);
+  host_zoom_levels_.clear();
+  const DictionaryValue* host_zoom_dictionary =
+      profile_->GetPrefs()->GetDictionary(prefs::kPerHostZoomLevels);
+  // Careful: The returned value could be NULL if the pref has never been set.
+  if (host_zoom_dictionary != NULL) {
+    for (DictionaryValue::key_iterator i(host_zoom_dictionary->begin_keys());
+         i != host_zoom_dictionary->end_keys(); ++i) {
+      const std::string& host(*i);
+      double zoom_level = 0;
+
+      bool success = host_zoom_dictionary->GetDoubleWithoutPathExpansion(
+          host, &zoom_level);
+      if (!success) {
+        // The data used to be stored as ints, so try that.
+        int int_zoom_level;
+        success = host_zoom_dictionary->GetIntegerWithoutPathExpansion(
+            host, &int_zoom_level);
+        if (success) {
+          zoom_level = static_cast<double>(int_zoom_level);
+          // Since the values were once stored as non-clamped, clamp now.
+          double zoom_factor = WebView::zoomLevelToZoomFactor(zoom_level);
+          if (zoom_factor < WebView::minTextSizeMultiplier) {
+            zoom_level =
+                WebView::zoomFactorToZoomLevel(WebView::minTextSizeMultiplier);
+          } else if (zoom_factor > WebView::maxTextSizeMultiplier) {
+            zoom_level =
+                WebView::zoomFactorToZoomLevel(WebView::maxTextSizeMultiplier);
+          }
+        }
+      }
+      DCHECK(success);
+      host_zoom_levels_[host] = zoom_level;
+    }
+  }
+}
+
+// static
+void HostZoomMap::RegisterUserPrefs(PrefService* prefs) {
+  prefs->RegisterDictionaryPref(prefs::kPerHostZoomLevels);
+}
+
+double HostZoomMap::GetZoomLevel(const GURL& url) const {
+  std::string host(net::GetHostOrSpecFromURL(url));
+  base::AutoLock auto_lock(lock_);
+  HostZoomLevels::const_iterator i(host_zoom_levels_.find(host));
+  return (i == host_zoom_levels_.end()) ? default_zoom_level_ : i->second;
+}
+
+void HostZoomMap::SetZoomLevel(const GURL& url, double level) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (!profile_)
+    return;
+
+  std::string host(net::GetHostOrSpecFromURL(url));
+
+  {
+    base::AutoLock auto_lock(lock_);
+    if (level == default_zoom_level_)
+      host_zoom_levels_.erase(host);
+    else
+      host_zoom_levels_[host] = level;
+  }
+
+  NotificationService::current()->Notify(NotificationType::ZOOM_LEVEL_CHANGED,
+                                         Source<Profile>(profile_),
+                                         NotificationService::NoDetails());
+
+  // If we're in incognito mode, don't persist changes to the prefs.  We'll keep
+  // them in memory only so they will be forgotten on exiting incognito.
+  if (profile_->IsOffTheRecord())
+    return;
+
+  updating_preferences_ = true;
+  {
+    ScopedPrefUpdate update(profile_->GetPrefs(), prefs::kPerHostZoomLevels);
+    DictionaryValue* host_zoom_dictionary =
+        profile_->GetPrefs()->GetMutableDictionary(prefs::kPerHostZoomLevels);
+    if (level == default_zoom_level_) {
+      host_zoom_dictionary->RemoveWithoutPathExpansion(host, NULL);
+    } else {
+      host_zoom_dictionary->SetWithoutPathExpansion(
+          host, Value::CreateDoubleValue(level));
+    }
+  }
+  updating_preferences_ = false;
+}
+
+double HostZoomMap::GetTemporaryZoomLevel(int render_process_id,
+                                          int render_view_id) const {
+  base::AutoLock auto_lock(lock_);
+  for (size_t i = 0; i < temporary_zoom_levels_.size(); ++i) {
+    if (temporary_zoom_levels_[i].render_process_id == render_process_id &&
+        temporary_zoom_levels_[i].render_view_id == render_view_id) {
+      return temporary_zoom_levels_[i].zoom_level;
+    }
+  }
+  return 0;
+}
+
+void HostZoomMap::SetTemporaryZoomLevel(int render_process_id,
+                                        int render_view_id,
+                                        double level) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (!profile_)
+    return;
+
+  {
+    base::AutoLock auto_lock(lock_);
+    size_t i;
+    for (i = 0; i < temporary_zoom_levels_.size(); ++i) {
+      if (temporary_zoom_levels_[i].render_process_id == render_process_id &&
+          temporary_zoom_levels_[i].render_view_id == render_view_id) {
+        if (level) {
+          temporary_zoom_levels_[i].zoom_level = level;
+        } else {
+          temporary_zoom_levels_.erase(temporary_zoom_levels_.begin() + i);
+        }
+        break;
+      }
+    }
+
+    if (level && i == temporary_zoom_levels_.size()) {
+      TemporaryZoomLevel temp;
+      temp.render_process_id = render_process_id;
+      temp.render_view_id = render_view_id;
+      temp.zoom_level = level;
+      temporary_zoom_levels_.push_back(temp);
+    }
+  }
+
+  NotificationService::current()->Notify(NotificationType::ZOOM_LEVEL_CHANGED,
+                                         Source<Profile>(profile_),
+                                         NotificationService::NoDetails());
+}
+
+void HostZoomMap::ResetToDefaults() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (!profile_)
+    return;
+
+  {
+    base::AutoLock auto_lock(lock_);
+    host_zoom_levels_.clear();
+  }
+
+  updating_preferences_ = true;
+  profile_->GetPrefs()->ClearPref(prefs::kPerHostZoomLevels);
+  updating_preferences_ = false;
+}
+
+void HostZoomMap::Shutdown() {
+  if (!profile_)
+    return;
+
+  registrar_.RemoveAll();
+  if (!profile_->IsOffTheRecord())
+    pref_change_registrar_.RemoveAll();
+  profile_ = NULL;
+}
+
+void HostZoomMap::Observe(
+    NotificationType type,
+    const NotificationSource& source,
+    const NotificationDetails& details) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  switch (type.value) {
+    case NotificationType::PROFILE_DESTROYED:
+      // If the profile is going away, we need to stop using it.
+      Shutdown();
+      break;
+    case NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW: {
+      base::AutoLock auto_lock(lock_);
+      int render_view_id = Source<RenderViewHost>(source)->routing_id();
+      int render_process_id = Source<RenderViewHost>(source)->process()->id();
+
+      for (size_t i = 0; i < temporary_zoom_levels_.size(); ++i) {
+        if (temporary_zoom_levels_[i].render_process_id == render_process_id &&
+            temporary_zoom_levels_[i].render_view_id == render_view_id) {
+          temporary_zoom_levels_.erase(temporary_zoom_levels_.begin() + i);
+          break;
+        }
+      }
+      break;
+    }
+    case NotificationType::PREF_CHANGED: {
+      // If we are updating our own preference, don't reload.
+      if (!updating_preferences_) {
+        std::string* name = Details<std::string>(details).ptr();
+        if (prefs::kPerHostZoomLevels == *name)
+          Load();
+        else if (prefs::kDefaultZoomLevel == *name) {
+          default_zoom_level_ =
+              profile_->GetPrefs()->GetDouble(prefs::kDefaultZoomLevel);
+        }
+      }
+      break;
+    }
+    default:
+      NOTREACHED() << "Unexpected preference observed.";
+  }
+}
+
+HostZoomMap::~HostZoomMap() {
+  Shutdown();
+}
diff --git a/content/browser/host_zoom_map.h b/content/browser/host_zoom_map.h
new file mode 100644
index 0000000..4951995
--- /dev/null
+++ b/content/browser/host_zoom_map.h
@@ -0,0 +1,127 @@
+// 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.
+
+// Maps hostnames to custom zoom levels.  Written on the UI thread and read on
+// any thread.  One instance per profile.
+
+#ifndef CONTENT_BROWSER_HOST_ZOOM_MAP_H_
+#define CONTENT_BROWSER_HOST_ZOOM_MAP_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/prefs/pref_change_registrar.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "content/browser/browser_thread.h"
+
+class GURL;
+class PrefService;
+class Profile;
+
+// HostZoomMap needs to be deleted on the UI thread because it listens
+// to notifications on there (and holds a NotificationRegistrar).
+class HostZoomMap :
+    public NotificationObserver,
+    public base::RefCountedThreadSafe<HostZoomMap,
+                                      BrowserThread::DeleteOnUIThread> {
+ public:
+  explicit HostZoomMap(Profile* profile);
+
+  static void RegisterUserPrefs(PrefService* prefs);
+
+  // Returns the zoom level for a given url. The zoom level is determined by
+  // the host portion of the URL, or (in the absence of a host) the complete
+  // spec of the URL. In most cases, there is no custom zoom level, and this
+  // returns the user's default zoom level.  Otherwise, returns the saved zoom
+  // level, which may be positive (to zoom in) or negative (to zoom out).
+  //
+  // This may be called on any thread.
+  double GetZoomLevel(const GURL& url) const;
+
+  // Sets the zoom level for a given url to |level|.  If the level matches the
+  // current default zoom level, the host is erased from the saved preferences;
+  // otherwise the new value is written out.
+  //
+  // This should only be called on the UI thread.
+  void SetZoomLevel(const GURL& url, double level);
+
+  // Returns the temporary zoom level that's only valid for the lifetime of
+  // the given tab (i.e. isn't saved and doesn't affect other tabs) if it
+  // exists, the default zoom level otherwise.
+  //
+  // This may be called on any thread.
+  double GetTemporaryZoomLevel(int render_process_id,
+                               int render_view_id) const;
+
+  // Sets the temporary zoom level that's only valid for the lifetime of this
+  // tab.
+  //
+  // This should only be called on the UI thread.
+  void SetTemporaryZoomLevel(int render_process_id,
+                             int render_view_id,
+                             double level);
+
+  // Resets all zoom levels.
+  //
+  // This should only be called on the UI thread.
+  void ResetToDefaults();
+
+  // NotificationObserver implementation.
+  virtual void Observe(NotificationType type,
+                       const NotificationSource& source,
+                       const NotificationDetails& details);
+
+ private:
+  friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
+  friend class DeleteTask<HostZoomMap>;
+
+  typedef std::map<std::string, double> HostZoomLevels;
+
+  ~HostZoomMap();
+
+  // Reads the zoom levels from the preferences service.
+  void Load();
+
+  // Removes dependencies on the profile so we can live longer than
+  // the profile without crashing.
+  void Shutdown();
+
+  // The profile we're associated with.
+  Profile* profile_;
+
+  // Copy of the pref data, so that we can read it on the IO thread.
+  HostZoomLevels host_zoom_levels_;
+  double default_zoom_level_;
+
+  struct TemporaryZoomLevel {
+    int render_process_id;
+    int render_view_id;
+    double zoom_level;
+  };
+
+  // Don't expect more than a couple of tabs that are using a temporary zoom
+  // level, so vector is fine for now.
+  std::vector<TemporaryZoomLevel> temporary_zoom_levels_;
+
+  // Used around accesses to |host_zoom_levels_|, |default_zoom_level_| and
+  // |temporary_zoom_levels_| to guarantee thread safety.
+  mutable base::Lock lock_;
+
+  // Whether we are currently updating preferences, this is used to ignore
+  // notifications from the preference service that we triggered ourself.
+  bool updating_preferences_;
+
+  NotificationRegistrar registrar_;
+  PrefChangeRegistrar pref_change_registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostZoomMap);
+};
+
+#endif  // CONTENT_BROWSER_HOST_ZOOM_MAP_H_
diff --git a/content/browser/host_zoom_map_unittest.cc b/content/browser/host_zoom_map_unittest.cc
new file mode 100644
index 0000000..82e0e58
--- /dev/null
+++ b/content/browser/host_zoom_map_unittest.cc
@@ -0,0 +1,132 @@
+// 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 "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/host_zoom_map.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_observer_mock.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/testing_profile.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Pointee;
+using testing::Property;
+
+class HostZoomMapTest : public testing::Test {
+ public:
+  static const double kZoomLevel;
+  static const double kDefaultZoomLevel;
+  HostZoomMapTest()
+      : ui_thread_(BrowserThread::UI, &message_loop_),
+        prefs_(profile_.GetPrefs()),
+        per_host_zoom_levels_pref_(prefs::kPerHostZoomLevels),
+        url_("http://example.com/test"),
+        host_("example.com") {}
+
+ protected:
+  void SetPrefObserverExpectation() {
+    EXPECT_CALL(
+        pref_observer_,
+        Observe(NotificationType(NotificationType::PREF_CHANGED),
+                _,
+                Property(&Details<std::string>::ptr,
+                         Pointee(per_host_zoom_levels_pref_))));
+  }
+
+  MessageLoopForUI message_loop_;
+  BrowserThread ui_thread_;
+  TestingProfile profile_;
+  PrefService* prefs_;
+  std::string per_host_zoom_levels_pref_;  // For the observe matcher.
+  GURL url_;
+  std::string host_;
+  NotificationObserverMock pref_observer_;
+};
+const double HostZoomMapTest::kZoomLevel = 4;
+const double HostZoomMapTest::kDefaultZoomLevel = -2;
+
+TEST_F(HostZoomMapTest, LoadNoPrefs) {
+  scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
+  EXPECT_EQ(0, map->GetZoomLevel(url_));
+}
+
+TEST_F(HostZoomMapTest, Load) {
+  DictionaryValue* dict =
+      prefs_->GetMutableDictionary(prefs::kPerHostZoomLevels);
+  dict->SetWithoutPathExpansion(host_, Value::CreateDoubleValue(kZoomLevel));
+  scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
+  EXPECT_EQ(kZoomLevel, map->GetZoomLevel(url_));
+}
+
+TEST_F(HostZoomMapTest, SetZoomLevel) {
+  scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
+  PrefChangeRegistrar registrar;
+  registrar.Init(prefs_);
+  registrar.Add(prefs::kPerHostZoomLevels, &pref_observer_);
+  SetPrefObserverExpectation();
+  map->SetZoomLevel(url_, kZoomLevel);
+  EXPECT_EQ(kZoomLevel, map->GetZoomLevel(url_));
+  const DictionaryValue* dict =
+      prefs_->GetDictionary(prefs::kPerHostZoomLevels);
+  double zoom_level = 0;
+  EXPECT_TRUE(dict->GetDoubleWithoutPathExpansion(host_, &zoom_level));
+  EXPECT_EQ(kZoomLevel, zoom_level);
+
+  SetPrefObserverExpectation();
+  map->SetZoomLevel(url_, 0);
+  EXPECT_EQ(0, map->GetZoomLevel(url_));
+  EXPECT_FALSE(dict->HasKey(host_));
+}
+
+TEST_F(HostZoomMapTest, ResetToDefaults) {
+  scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
+  map->SetZoomLevel(url_, kZoomLevel);
+
+  PrefChangeRegistrar registrar;
+  registrar.Init(prefs_);
+  registrar.Add(prefs::kPerHostZoomLevels, &pref_observer_);
+  SetPrefObserverExpectation();
+  map->ResetToDefaults();
+  EXPECT_EQ(0, map->GetZoomLevel(url_));
+  DictionaryValue empty;
+  EXPECT_TRUE(
+      Value::Equals(&empty, prefs_->GetDictionary(prefs::kPerHostZoomLevels)));
+}
+
+TEST_F(HostZoomMapTest, ReloadOnPrefChange) {
+  scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
+  map->SetZoomLevel(url_, kZoomLevel);
+
+  DictionaryValue dict;
+  dict.SetWithoutPathExpansion(host_, Value::CreateDoubleValue(0));
+  prefs_->Set(prefs::kPerHostZoomLevels, dict);
+  EXPECT_EQ(0, map->GetZoomLevel(url_));
+}
+
+TEST_F(HostZoomMapTest, NoHost) {
+  scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
+  GURL file_url1_("file:///tmp/test.html");
+  GURL file_url2_("file:///tmp/other.html");
+  map->SetZoomLevel(file_url1_, kZoomLevel);
+
+  EXPECT_EQ(kZoomLevel, map->GetZoomLevel(file_url1_));
+  EXPECT_EQ(0, map->GetZoomLevel(file_url2_));
+}
+
+TEST_F(HostZoomMapTest, ChangeDefaultZoomLevel) {
+  FundamentalValue zoom_level(kDefaultZoomLevel);
+  prefs_->Set(prefs::kDefaultZoomLevel, zoom_level);
+  scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
+  EXPECT_EQ(kDefaultZoomLevel, map->GetZoomLevel(url_));
+}
diff --git a/content/browser/mime_registry_message_filter.cc b/content/browser/mime_registry_message_filter.cc
new file mode 100644
index 0000000..3268b84
--- /dev/null
+++ b/content/browser/mime_registry_message_filter.cc
@@ -0,0 +1,51 @@
+// 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/mime_registry_message_filter.h"
+
+#include "chrome/common/mime_registry_messages.h"
+#include "net/base/mime_util.h"
+
+MimeRegistryMessageFilter::MimeRegistryMessageFilter() {
+}
+
+MimeRegistryMessageFilter::~MimeRegistryMessageFilter() {
+}
+
+void MimeRegistryMessageFilter::OverrideThreadForMessage(
+    const IPC::Message& message,
+    BrowserThread::ID* thread) {
+  if (IPC_MESSAGE_CLASS(message) == MimeRegistryMsgStart)
+    *thread = BrowserThread::FILE;
+}
+
+bool MimeRegistryMessageFilter::OnMessageReceived(const IPC::Message& message,
+                                                  bool* message_was_ok) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP_EX(MimeRegistryMessageFilter, message, *message_was_ok)
+    IPC_MESSAGE_HANDLER(MimeRegistryMsg_GetMimeTypeFromExtension,
+                        OnGetMimeTypeFromExtension)
+    IPC_MESSAGE_HANDLER(MimeRegistryMsg_GetMimeTypeFromFile,
+                        OnGetMimeTypeFromFile)
+    IPC_MESSAGE_HANDLER(MimeRegistryMsg_GetPreferredExtensionForMimeType,
+                        OnGetPreferredExtensionForMimeType)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void MimeRegistryMessageFilter::OnGetMimeTypeFromExtension(
+    const FilePath::StringType& ext, std::string* mime_type) {
+  net::GetMimeTypeFromExtension(ext, mime_type);
+}
+
+void MimeRegistryMessageFilter::OnGetMimeTypeFromFile(
+    const FilePath& file_path, std::string* mime_type) {
+  net::GetMimeTypeFromFile(file_path, mime_type);
+}
+
+void MimeRegistryMessageFilter::OnGetPreferredExtensionForMimeType(
+    const std::string& mime_type, FilePath::StringType* extension) {
+  net::GetPreferredExtensionForMimeType(mime_type, extension);
+}
diff --git a/content/browser/mime_registry_message_filter.h b/content/browser/mime_registry_message_filter.h
new file mode 100644
index 0000000..87b5b24d
--- /dev/null
+++ b/content/browser/mime_registry_message_filter.h
@@ -0,0 +1,31 @@
+// 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_MIME_REGISTRY_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_MIME_REGISTRY_MESSAGE_FILTER_H_
+
+#include "base/file_path.h"
+#include "chrome/browser/browser_message_filter.h"
+
+class MimeRegistryMessageFilter : public BrowserMessageFilter {
+ public:
+  MimeRegistryMessageFilter();
+
+  virtual void OverrideThreadForMessage(const IPC::Message& message,
+                                        BrowserThread::ID* thread);
+  virtual bool OnMessageReceived(const IPC::Message& message,
+                                 bool* message_was_ok);
+
+ private:
+  ~MimeRegistryMessageFilter();
+
+  void OnGetMimeTypeFromExtension(const FilePath::StringType& ext,
+                                  std::string* mime_type);
+  void OnGetMimeTypeFromFile(const FilePath& file_path,
+                             std::string* mime_type);
+  void OnGetPreferredExtensionForMimeType(const std::string& mime_type,
+                                          FilePath::StringType* extension);
+};
+
+#endif  // CONTENT_BROWSER_MIME_REGISTRY_MESSAGE_FILTER_H_
diff --git a/content/browser/modal_html_dialog_delegate.cc b/content/browser/modal_html_dialog_delegate.cc
new file mode 100644
index 0000000..0f2ca4d
--- /dev/null
+++ b/content/browser/modal_html_dialog_delegate.cc
@@ -0,0 +1,77 @@
+// 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/modal_html_dialog_delegate.h"
+
+#include <string>
+
+#include "chrome/browser/browser_list.h"
+#include "chrome/common/notification_source.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "ui/gfx/size.h"
+
+ModalHtmlDialogDelegate::ModalHtmlDialogDelegate(
+    const GURL& url, int width, int height, const std::string& json_arguments,
+    IPC::Message* sync_result, TabContents* contents)
+    : contents_(contents),
+      sync_response_(sync_result) {
+  // Listen for when the TabContents or its renderer dies.
+  registrar_.Add(this, NotificationType::TAB_CONTENTS_DISCONNECTED,
+                 Source<TabContents>(contents_));
+
+  // This information is needed to show the dialog HTML content.
+  params_.url = url;
+  params_.height = height;
+  params_.width = width;
+  params_.json_input = json_arguments;
+}
+
+ModalHtmlDialogDelegate::~ModalHtmlDialogDelegate() {
+}
+
+void ModalHtmlDialogDelegate::Observe(NotificationType type,
+                                      const NotificationSource& source,
+                                      const NotificationDetails& details) {
+  DCHECK(type == NotificationType::TAB_CONTENTS_DISCONNECTED);
+  DCHECK(Source<TabContents>(source).ptr() == contents_);
+  registrar_.RemoveAll();
+  contents_ = NULL;
+}
+
+bool ModalHtmlDialogDelegate::IsDialogModal() const {
+  return true;
+}
+
+std::wstring ModalHtmlDialogDelegate::GetDialogTitle() const {
+  return L"Gears";
+}
+
+GURL ModalHtmlDialogDelegate::GetDialogContentURL() const {
+  return params_.url;
+}
+
+void ModalHtmlDialogDelegate::GetDialogSize(gfx::Size* size) const {
+  size->set_width(params_.width);
+  size->set_height(params_.height);
+}
+
+std::string ModalHtmlDialogDelegate::GetDialogArgs() const {
+  return params_.json_input;
+}
+
+void ModalHtmlDialogDelegate::OnDialogClosed(const std::string& json_retval) {
+  // Our TabContents may have died before this point.
+  if (contents_ && contents_->render_view_host()) {
+    contents_->render_view_host()->ModalHTMLDialogClosed(sync_response_,
+                                                         json_retval);
+  }
+
+  // We are done with this request, so delete us.
+  delete this;
+}
+
+bool ModalHtmlDialogDelegate::ShouldShowDialogTitle() const {
+  return true;
+}
diff --git a/content/browser/modal_html_dialog_delegate.h b/content/browser/modal_html_dialog_delegate.h
new file mode 100644
index 0000000..642acd8
--- /dev/null
+++ b/content/browser/modal_html_dialog_delegate.h
@@ -0,0 +1,68 @@
+// 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_MODAL_HTML_DIALOG_DELEGATE_H_
+#define CONTENT_BROWSER_MODAL_HTML_DIALOG_DELEGATE_H_
+#pragma once
+
+#include <vector>
+
+#include "chrome/browser/webui/html_dialog_ui.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+namespace gfx {
+class Size;
+}
+
+namespace IPC {
+class Message;
+}
+
+// This class can only be used on the UI thread.
+class ModalHtmlDialogDelegate
+    : public HtmlDialogUIDelegate,
+      public NotificationObserver {
+ public:
+  ModalHtmlDialogDelegate(const GURL& url,
+                          int width, int height,
+                          const std::string& json_arguments,
+                          IPC::Message* sync_result,
+                          TabContents* contents);
+  ~ModalHtmlDialogDelegate();
+
+  // Notification service callback.
+  virtual void Observe(NotificationType type,
+                       const NotificationSource& source,
+                       const NotificationDetails& details);
+
+  // HTMLDialogUIDelegate implementation:
+  virtual bool IsDialogModal() const;
+  virtual std::wstring GetDialogTitle() const;
+  virtual GURL GetDialogContentURL() const;
+  virtual void GetWebUIMessageHandlers(
+      std::vector<WebUIMessageHandler*>* handlers) const { }
+  virtual void GetDialogSize(gfx::Size* size) const;
+  virtual std::string GetDialogArgs() const;
+  virtual void OnDialogClosed(const std::string& json_retval);
+  virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { }
+  virtual bool ShouldShowDialogTitle() const;
+
+ private:
+  NotificationRegistrar registrar_;
+
+  // The TabContents that opened the dialog.
+  TabContents* contents_;
+
+  // The parameters needed to display a modal HTML dialog.
+  HtmlDialogUI::HtmlDialogParams params_;
+
+  // Once we get our reply in OnModalDialogResponse we'll need to respond to the
+  // plugin using this |sync_result| pointer so we store it between calls.
+  IPC::Message* sync_response_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModalHtmlDialogDelegate);
+};
+
+#endif  // CONTENT_BROWSER_MODAL_HTML_DIALOG_DELEGATE_H_
diff --git a/content/browser/plugin_process_host.cc b/content/browser/plugin_process_host.cc
new file mode 100644
index 0000000..215f653
--- /dev/null
+++ b/content/browser/plugin_process_host.cc
@@ -0,0 +1,478 @@
+// 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/plugin_process_host.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <utility>  // for pair<>
+#endif
+
+#include <vector>
+
+#include "app/app_switches.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/chrome_plugin_browsing_context.h"
+#include "chrome/browser/net/url_request_tracking.h"
+#include "chrome/browser/plugin_download_helper.h"
+#include "chrome/browser/plugin_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_plugin_lib.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/logging_chrome.h"
+#include "chrome/common/net/url_request_context_getter.h"
+#include "chrome/common/plugin_messages.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/child_process_security_policy.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/renderer_host/resource_message_filter.h"
+#include "ipc/ipc_switches.h"
+#include "net/base/cookie_store.h"
+#include "net/base/io_buffer.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "ui/base/ui_base_switches.h"
+#include "ui/gfx/native_widget_types.h"
+
+#if defined(USE_X11)
+#include "ui/gfx/gtk_native_view_id_manager.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#include "chrome/common/plugin_carbon_interpose_constants_mac.h"
+#include "ui/gfx/rect.h"
+#endif
+
+static const char kDefaultPluginFinderURL[] =
+    "https://dl-ssl.google.com/edgedl/chrome/plugins/plugins2.xml";
+
+namespace {
+
+// Helper class that we pass to ResourceMessageFilter so that it can find the
+// right net::URLRequestContext for a request.
+class PluginURLRequestContextOverride
+    : public ResourceMessageFilter::URLRequestContextOverride {
+ public:
+  PluginURLRequestContextOverride() {
+  }
+
+  virtual net::URLRequestContext* GetRequestContext(
+      const ViewHostMsg_Resource_Request& resource_request) {
+    return CPBrowsingContextManager::GetInstance()->ToURLRequestContext(
+        resource_request.request_context);
+  }
+
+ private:
+  virtual ~PluginURLRequestContextOverride() {}
+};
+
+}  // namespace
+
+#if defined(OS_WIN)
+void PluginProcessHost::OnPluginWindowDestroyed(HWND window, HWND parent) {
+  // The window is destroyed at this point, we just care about its parent, which
+  // is the intermediate window we created.
+  std::set<HWND>::iterator window_index =
+      plugin_parent_windows_set_.find(parent);
+  if (window_index == plugin_parent_windows_set_.end())
+    return;
+
+  plugin_parent_windows_set_.erase(window_index);
+  PostMessage(parent, WM_CLOSE, 0, 0);
+}
+
+void PluginProcessHost::OnDownloadUrl(const std::string& url,
+                                      int source_pid,
+                                      gfx::NativeWindow caller_window) {
+  PluginDownloadUrlHelper* download_url_helper =
+      new PluginDownloadUrlHelper(url, source_pid, caller_window, NULL);
+  download_url_helper->InitiateDownload(
+      Profile::GetDefaultRequestContext()->GetURLRequestContext());
+}
+
+void PluginProcessHost::AddWindow(HWND window) {
+  plugin_parent_windows_set_.insert(window);
+}
+
+#endif  // defined(OS_WIN)
+
+#if defined(TOOLKIT_USES_GTK)
+void PluginProcessHost::OnMapNativeViewId(gfx::NativeViewId id,
+                                          gfx::PluginWindowHandle* output) {
+  *output = 0;
+  GtkNativeViewManager::GetInstance()->GetXIDForId(output, id);
+}
+#endif  // defined(TOOLKIT_USES_GTK)
+
+PluginProcessHost::PluginProcessHost()
+    : BrowserChildProcessHost(
+          PLUGIN_PROCESS,
+          PluginService::GetInstance()->resource_dispatcher_host(),
+          new PluginURLRequestContextOverride()),
+      ALLOW_THIS_IN_INITIALIZER_LIST(resolve_proxy_msg_helper_(this, NULL))
+#if defined(OS_MACOSX)
+      , plugin_cursor_visible_(true)
+#endif
+{
+}
+
+PluginProcessHost::~PluginProcessHost() {
+#if defined(OS_WIN)
+  // We erase HWNDs from the plugin_parent_windows_set_ when we receive a
+  // notification that the window is being destroyed. If we don't receive this
+  // notification and the PluginProcessHost instance is being destroyed, it
+  // means that the plugin process crashed. We paint a sad face in this case in
+  // the renderer process. To ensure that the sad face shows up, and we don't
+  // leak HWNDs, we should destroy existing plugin parent windows.
+  std::set<HWND>::iterator window_index;
+  for (window_index = plugin_parent_windows_set_.begin();
+       window_index != plugin_parent_windows_set_.end();
+       window_index++) {
+    PostMessage(*window_index, WM_CLOSE, 0, 0);
+  }
+#elif defined(OS_MACOSX)
+  // If the plugin process crashed but had fullscreen windows open at the time,
+  // make sure that the menu bar is visible.
+  std::set<uint32>::iterator window_index;
+  for (window_index = plugin_fullscreen_windows_set_.begin();
+       window_index != plugin_fullscreen_windows_set_.end();
+       window_index++) {
+    if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+      base::mac::ReleaseFullScreen(base::mac::kFullScreenModeHideAll);
+    } else {
+      BrowserThread::PostTask(
+          BrowserThread::UI, FROM_HERE,
+          NewRunnableFunction(base::mac::ReleaseFullScreen,
+                              base::mac::kFullScreenModeHideAll));
+    }
+  }
+  // If the plugin hid the cursor, reset that.
+  if (!plugin_cursor_visible_) {
+    if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+      base::mac::SetCursorVisibility(true);
+    } else {
+      BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        NewRunnableFunction(base::mac::SetCursorVisibility,
+                            true));
+    }
+  }
+#endif
+  // Cancel all pending and sent requests.
+  CancelRequests();
+}
+
+bool PluginProcessHost::Init(const webkit::npapi::WebPluginInfo& info,
+                             const std::string& locale) {
+  info_ = info;
+  set_name(UTF16ToWideHack(info_.name));
+  set_version(UTF16ToWideHack(info_.version));
+
+  if (!CreateChannel())
+    return false;
+
+  // Build command line for plugin. When we have a plugin launcher, we can't
+  // allow "self" on linux and we need the real file path.
+  const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+  CommandLine::StringType plugin_launcher =
+      browser_command_line.GetSwitchValueNative(switches::kPluginLauncher);
+  FilePath exe_path = GetChildPath(plugin_launcher.empty());
+  if (exe_path.empty())
+    return false;
+
+  CommandLine* cmd_line = new CommandLine(exe_path);
+  // Put the process type and plugin path first so they're easier to see
+  // in process listings using native process management tools.
+  cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kPluginProcess);
+  cmd_line->AppendSwitchPath(switches::kPluginPath, info.path);
+
+  if (logging::DialogsAreSuppressed())
+    cmd_line->AppendSwitch(switches::kNoErrorDialogs);
+
+  // Propagate the following switches to the plugin command line (along with
+  // any associated values) if present in the browser command line
+  static const char* const kSwitchNames[] = {
+    switches::kPluginStartupDialog,
+    switches::kNoSandbox,
+    switches::kSafePlugins,
+    switches::kTestSandbox,
+    switches::kUserAgent,
+    switches::kDisableBreakpad,
+    switches::kFullMemoryCrashReport,
+    switches::kEnableLogging,
+    switches::kDisableLogging,
+    switches::kLoggingLevel,
+    switches::kLogPluginMessages,
+    switches::kUserDataDir,
+    switches::kEnableDCHECK,
+    switches::kSilentDumpOnDCHECK,
+    switches::kMemoryProfiling,
+    switches::kUseLowFragHeapCrt,
+    switches::kEnableStatsTable,
+    switches::kEnableGPUPlugin,
+    switches::kUseGL,
+#if defined(OS_CHROMEOS)
+    switches::kLoginProfile,
+#endif
+  };
+
+  cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
+                             arraysize(kSwitchNames));
+
+  // If specified, prepend a launcher program to the command line.
+  if (!plugin_launcher.empty())
+    cmd_line->PrependWrapper(plugin_launcher);
+
+  if (!locale.empty()) {
+    // Pass on the locale so the null plugin will use the right language in the
+    // prompt to install the desired plugin.
+    cmd_line->AppendSwitchASCII(switches::kLang, locale);
+  }
+
+  // Gears requires the data dir to be available on startup.
+  FilePath data_dir =
+    PluginService::GetInstance()->GetChromePluginDataDir();
+  DCHECK(!data_dir.empty());
+  cmd_line->AppendSwitchPath(switches::kPluginDataDir, data_dir);
+
+  cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id());
+
+  SetCrashReporterCommandLine(cmd_line);
+
+#if defined(OS_POSIX)
+  base::environment_vector env;
+#if defined(OS_MACOSX) && !defined(__LP64__)
+  // Add our interposing library for Carbon. This is stripped back out in
+  // plugin_main.cc, so changes here should be reflected there.
+  std::string interpose_list(plugin_interpose_strings::kInterposeLibraryPath);
+  const char* existing_list =
+      getenv(plugin_interpose_strings::kDYLDInsertLibrariesKey);
+  if (existing_list) {
+    interpose_list.insert(0, ":");
+    interpose_list.insert(0, existing_list);
+  }
+  env.push_back(std::pair<std::string, std::string>(
+      plugin_interpose_strings::kDYLDInsertLibrariesKey,
+      interpose_list));
+#endif
+#endif
+
+  Launch(
+#if defined(OS_WIN)
+      FilePath(),
+#elif defined(OS_POSIX)
+      false,
+      env,
+#endif
+      cmd_line);
+
+  return true;
+}
+
+void PluginProcessHost::ForceShutdown() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  Send(new PluginProcessMsg_NotifyRenderersOfPendingShutdown());
+  BrowserChildProcessHost::ForceShutdown();
+}
+
+void PluginProcessHost::OnProcessLaunched() {
+  FilePath gears_path;
+  if (PathService::Get(chrome::FILE_GEARS_PLUGIN, &gears_path)) {
+    FilePath::StringType gears_path_lc = StringToLowerASCII(gears_path.value());
+    FilePath::StringType plugin_path_lc =
+        StringToLowerASCII(info_.path.value());
+    if (plugin_path_lc == gears_path_lc) {
+      // Give Gears plugins "background" priority.  See http://b/1280317.
+      SetProcessBackgrounded();
+    }
+  }
+}
+
+bool PluginProcessHost::OnMessageReceived(const IPC::Message& msg) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(PluginProcessHost, msg)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelCreated, OnChannelCreated)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_GetPluginFinderUrl,
+                        OnGetPluginFinderUrl)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginMessage, OnPluginMessage)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_GetCookies, OnGetCookies)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_AccessFiles, OnAccessFiles)
+    IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginProcessHostMsg_ResolveProxy,
+                                    OnResolveProxy)
+#if defined(OS_WIN)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginWindowDestroyed,
+                        OnPluginWindowDestroyed)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_DownloadUrl, OnDownloadUrl)
+#endif
+#if defined(TOOLKIT_USES_GTK)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_MapNativeViewId,
+                        OnMapNativeViewId)
+#endif
+#if defined(OS_MACOSX)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSelectWindow,
+                        OnPluginSelectWindow)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginShowWindow,
+                        OnPluginShowWindow)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginHideWindow,
+                        OnPluginHideWindow)
+    IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSetCursorVisibility,
+                        OnPluginSetCursorVisibility)
+#endif
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+
+  DCHECK(handled);
+  return handled;
+}
+
+void PluginProcessHost::OnChannelConnected(int32 peer_pid) {
+  for (size_t i = 0; i < pending_requests_.size(); ++i) {
+    RequestPluginChannel(pending_requests_[i]);
+  }
+
+  pending_requests_.clear();
+}
+
+void PluginProcessHost::OnChannelError() {
+  CancelRequests();
+}
+
+bool PluginProcessHost::CanShutdown() {
+  return sent_requests_.empty();
+}
+
+void PluginProcessHost::CancelRequests() {
+  for (size_t i = 0; i < pending_requests_.size(); ++i)
+    pending_requests_[i]->OnError();
+  pending_requests_.clear();
+
+  while (!sent_requests_.empty()) {
+    sent_requests_.front()->OnError();
+    sent_requests_.pop();
+  }
+}
+
+void PluginProcessHost::OpenChannelToPlugin(Client* client) {
+  InstanceCreated();
+  client->SetPluginInfo(info_);
+  if (opening_channel()) {
+    // The channel is already in the process of being opened.  Put
+    // this "open channel" request into a queue of requests that will
+    // be run once the channel is open.
+    pending_requests_.push_back(client);
+    return;
+  }
+
+  // We already have an open channel, send a request right away to plugin.
+  RequestPluginChannel(client);
+}
+
+void PluginProcessHost::OnGetCookies(uint32 request_context,
+                                     const GURL& url,
+                                     std::string* cookies) {
+  net::URLRequestContext* context = CPBrowsingContextManager::GetInstance()->
+        ToURLRequestContext(request_context);
+  // TODO(mpcomplete): remove fallback case when Gears support is prevalent.
+  if (!context)
+    context = Profile::GetDefaultRequestContext()->GetURLRequestContext();
+
+  // Note: We don't have a first_party_for_cookies check because plugins bypass
+  // third-party cookie blocking.
+  if (context && context->cookie_store()) {
+    *cookies = context->cookie_store()->GetCookies(url);
+  } else {
+    DLOG(ERROR) << "Could not serve plugin cookies request.";
+    cookies->clear();
+  }
+}
+
+void PluginProcessHost::OnAccessFiles(int renderer_id,
+                                      const std::vector<std::string>& files,
+                                      bool* allowed) {
+  ChildProcessSecurityPolicy* policy =
+      ChildProcessSecurityPolicy::GetInstance();
+
+  for (size_t i = 0; i < files.size(); ++i) {
+    const FilePath path = FilePath::FromWStringHack(UTF8ToWide(files[i]));
+    if (!policy->CanReadFile(renderer_id, path)) {
+      VLOG(1) << "Denied unauthorized request for file " << files[i];
+      *allowed = false;
+      return;
+    }
+  }
+
+  *allowed = true;
+}
+
+void PluginProcessHost::OnResolveProxy(const GURL& url,
+                                       IPC::Message* reply_msg) {
+  resolve_proxy_msg_helper_.Start(url, reply_msg);
+}
+
+void PluginProcessHost::OnResolveProxyCompleted(IPC::Message* reply_msg,
+                                                int result,
+                                                const std::string& proxy_list) {
+  PluginProcessHostMsg_ResolveProxy::WriteReplyParams(
+      reply_msg, result, proxy_list);
+  Send(reply_msg);
+}
+
+void PluginProcessHost::RequestPluginChannel(Client* client) {
+  // We can't send any sync messages from the browser because it might lead to
+  // a hang.  However this async messages must be answered right away by the
+  // plugin process (i.e. unblocks a Send() call like a sync message) otherwise
+  // a deadlock can occur if the plugin creation request from the renderer is
+  // a result of a sync message by the plugin process.
+  PluginProcessMsg_CreateChannel* msg =
+      new PluginProcessMsg_CreateChannel(client->ID(),
+                                         client->OffTheRecord());
+  msg->set_unblock(true);
+  if (Send(msg)) {
+    sent_requests_.push(client);
+  } else {
+    client->OnError();
+  }
+}
+
+void PluginProcessHost::OnChannelCreated(
+    const IPC::ChannelHandle& channel_handle) {
+  Client* client = sent_requests_.front();
+
+  client->OnChannelOpened(channel_handle);
+  sent_requests_.pop();
+}
+
+void PluginProcessHost::OnGetPluginFinderUrl(std::string* plugin_finder_url) {
+  if (!plugin_finder_url) {
+    NOTREACHED();
+    return;
+  }
+
+  // TODO(iyengar)  Add the plumbing to retrieve the default
+  // plugin finder URL.
+  *plugin_finder_url = kDefaultPluginFinderURL;
+}
+
+void PluginProcessHost::OnPluginMessage(
+    const std::vector<uint8>& data) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  ChromePluginLib *chrome_plugin = ChromePluginLib::Find(info_.path);
+  if (chrome_plugin) {
+    void *data_ptr = const_cast<void*>(reinterpret_cast<const void*>(&data[0]));
+    uint32 data_len = static_cast<uint32>(data.size());
+    chrome_plugin->functions().on_message(data_ptr, data_len);
+  }
+}
diff --git a/content/browser/plugin_process_host.h b/content/browser/plugin_process_host.h
new file mode 100644
index 0000000..ad44aae6
--- /dev/null
+++ b/content/browser/plugin_process_host.h
@@ -0,0 +1,177 @@
+// 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_PLUGIN_PROCESS_HOST_H_
+#define CONTENT_BROWSER_PLUGIN_PROCESS_HOST_H_
+#pragma once
+
+#include "build/build_config.h"
+
+#include <queue>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "chrome/browser/net/resolve_proxy_msg_helper.h"
+#include "content/browser/browser_child_process_host.h"
+#include "ui/gfx/native_widget_types.h"
+#include "webkit/plugins/npapi/webplugininfo.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace IPC {
+struct ChannelHandle;
+}
+
+class GURL;
+
+// Represents the browser side of the browser <--> plugin communication
+// channel.  Different plugins run in their own process, but multiple instances
+// of the same plugin run in the same process.  There will be one
+// PluginProcessHost per plugin process, matched with a corresponding
+// PluginProcess running in the plugin process.  The browser is responsible for
+// starting the plugin process when a plugin is created that doesn't already
+// have a process.  After that, most of the communication is directly between
+// the renderer and plugin processes.
+class PluginProcessHost : public BrowserChildProcessHost,
+                          public ResolveProxyMsgHelper::Delegate {
+ public:
+  class Client {
+   public:
+    // Returns a opaque unique identifier for the process requesting
+    // the channel.
+    virtual int ID() = 0;
+    virtual bool OffTheRecord() = 0;
+    virtual void SetPluginInfo(const webkit::npapi::WebPluginInfo& info) = 0;
+    // The client should delete itself when one of these methods is called.
+    virtual void OnChannelOpened(const IPC::ChannelHandle& handle) = 0;
+    virtual void OnError() = 0;
+
+   protected:
+    virtual ~Client() {}
+  };
+
+  PluginProcessHost();
+  virtual ~PluginProcessHost();
+
+  // Initialize the new plugin process, returning true on success. This must
+  // be called before the object can be used.
+  bool Init(const webkit::npapi::WebPluginInfo& info, const std::string& locale);
+
+  // Force the plugin process to shutdown (cleanly).
+  virtual void ForceShutdown();
+
+  virtual bool OnMessageReceived(const IPC::Message& msg);
+  virtual void OnChannelConnected(int32 peer_pid);
+  virtual void OnChannelError();
+
+  // ResolveProxyMsgHelper::Delegate implementation:
+  virtual void OnResolveProxyCompleted(IPC::Message* reply_msg,
+                                       int result,
+                                       const std::string& proxy_list);
+
+  // Tells the plugin process to create a new channel for communication with a
+  // renderer.  When the plugin process responds with the channel name,
+  // OnChannelOpened in the client is called.
+  void OpenChannelToPlugin(Client* client);
+
+  // This function is called on the IO thread once we receive a reply from the
+  // modal HTML dialog (in the form of a JSON string). This function forwards
+  // that reply back to the plugin that requested the dialog.
+  void OnModalDialogResponse(const std::string& json_retval,
+                             IPC::Message* sync_result);
+
+#if defined(OS_MACOSX)
+  // This function is called on the IO thread when the browser becomes the
+  // active application.
+  void OnAppActivation();
+#endif
+
+  const webkit::npapi::WebPluginInfo& info() const { return info_; }
+
+#if defined(OS_WIN)
+  // Tracks plugin parent windows created on the browser UI thread.
+  void AddWindow(HWND window);
+#endif
+
+ private:
+  friend class PluginResolveProxyHelper;
+
+  // Sends a message to the plugin process to request creation of a new channel
+  // for the given mime type.
+  void RequestPluginChannel(Client* client);
+
+  virtual void OnProcessLaunched();
+
+  // Message handlers.
+  void OnChannelCreated(const IPC::ChannelHandle& channel_handle);
+  void OnGetPluginFinderUrl(std::string* plugin_finder_url);
+  void OnGetCookies(uint32 request_context, const GURL& url,
+                    std::string* cookies);
+  void OnAccessFiles(int renderer_id, const std::vector<std::string>& files,
+                     bool* allowed);
+  void OnResolveProxy(const GURL& url, IPC::Message* reply_msg);
+  void OnPluginMessage(const std::vector<uint8>& data);
+
+#if defined(OS_WIN)
+  void OnPluginWindowDestroyed(HWND window, HWND parent);
+  void OnDownloadUrl(const std::string& url, int source_child_unique_id,
+                     gfx::NativeWindow caller_window);
+#endif
+
+#if defined(USE_X11)
+  void OnMapNativeViewId(gfx::NativeViewId id, gfx::PluginWindowHandle* output);
+#endif
+
+#if defined(OS_MACOSX)
+  void OnPluginSelectWindow(uint32 window_id, gfx::Rect window_rect,
+                            bool modal);
+  void OnPluginShowWindow(uint32 window_id, gfx::Rect window_rect,
+                          bool modal);
+  void OnPluginHideWindow(uint32 window_id, gfx::Rect window_rect);
+  void OnPluginSetCursorVisibility(bool visible);
+#endif
+
+  virtual bool CanShutdown();
+
+  void CancelRequests();
+
+  // These are channel requests that we are waiting to send to the
+  // plugin process once the channel is opened.
+  std::vector<Client*> pending_requests_;
+
+  // These are the channel requests that we have already sent to
+  // the plugin process, but haven't heard back about yet.
+  std::queue<Client*> sent_requests_;
+
+  // Information about the plugin.
+  webkit::npapi::WebPluginInfo info_;
+
+  // Helper class for handling PluginProcessHost_ResolveProxy messages (manages
+  // the requests to the proxy service).
+  ResolveProxyMsgHelper resolve_proxy_msg_helper_;
+
+#if defined(OS_WIN)
+  // Tracks plugin parent windows created on the UI thread.
+  std::set<HWND> plugin_parent_windows_set_;
+#endif
+#if defined(OS_MACOSX)
+  // Tracks plugin windows currently visible.
+  std::set<uint32> plugin_visible_windows_set_;
+  // Tracks full screen windows currently visible.
+  std::set<uint32> plugin_fullscreen_windows_set_;
+  // Tracks modal windows currently visible.
+  std::set<uint32> plugin_modal_windows_set_;
+  // Tracks the current visibility of the cursor.
+  bool plugin_cursor_visible_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(PluginProcessHost);
+};
+
+#endif  // CONTENT_BROWSER_PLUGIN_PROCESS_HOST_H_
diff --git a/content/browser/plugin_process_host_mac.cc b/content/browser/plugin_process_host_mac.cc
new file mode 100644
index 0000000..b329f2b
--- /dev/null
+++ b/content/browser/plugin_process_host_mac.cc
@@ -0,0 +1,111 @@
+// 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 <Carbon/Carbon.h>
+
+#include "build/build_config.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "chrome/common/plugin_messages.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/plugin_process_host.h"
+#include "ui/gfx/rect.h"
+
+void PluginProcessHost::OnPluginSelectWindow(uint32 window_id,
+                                             gfx::Rect window_rect,
+                                             bool modal) {
+  plugin_visible_windows_set_.insert(window_id);
+  if (modal)
+    plugin_modal_windows_set_.insert(window_id);
+}
+
+void PluginProcessHost::OnPluginShowWindow(uint32 window_id,
+                                           gfx::Rect window_rect,
+                                           bool modal) {
+  plugin_visible_windows_set_.insert(window_id);
+  if (modal)
+    plugin_modal_windows_set_.insert(window_id);
+  CGRect window_bounds = {
+    { window_rect.x(), window_rect.y() },
+    { window_rect.width(), window_rect.height() }
+  };
+  CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());
+  if (CGRectEqualToRect(window_bounds, main_display_bounds) &&
+      (plugin_fullscreen_windows_set_.find(window_id) ==
+       plugin_fullscreen_windows_set_.end())) {
+    plugin_fullscreen_windows_set_.insert(window_id);
+    // If the plugin has just shown a window that's the same dimensions as
+    // the main display, hide the menubar so that it has the whole screen.
+    // (but only if we haven't already seen this fullscreen window, since
+    // otherwise our refcounting can get skewed).
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        NewRunnableFunction(base::mac::RequestFullScreen,
+                            base::mac::kFullScreenModeHideAll));
+  }
+}
+
+// Must be called on the UI thread.
+// If plugin_pid is -1, the browser will be the active process on return,
+// otherwise that process will be given focus back before this function returns.
+static void ReleasePluginFullScreen(pid_t plugin_pid) {
+  // Releasing full screen only works if we are the frontmost process; grab
+  // focus, but give it back to the plugin process if requested.
+  base::mac::ActivateProcess(base::GetCurrentProcId());
+  base::mac::ReleaseFullScreen(base::mac::kFullScreenModeHideAll);
+  if (plugin_pid != -1) {
+    base::mac::ActivateProcess(plugin_pid);
+  }
+}
+
+void PluginProcessHost::OnPluginHideWindow(uint32 window_id,
+                                           gfx::Rect window_rect) {
+  bool had_windows = !plugin_visible_windows_set_.empty();
+  plugin_visible_windows_set_.erase(window_id);
+  bool browser_needs_activation = had_windows &&
+      plugin_visible_windows_set_.empty();
+
+  plugin_modal_windows_set_.erase(window_id);
+  if (plugin_fullscreen_windows_set_.find(window_id) !=
+      plugin_fullscreen_windows_set_.end()) {
+    plugin_fullscreen_windows_set_.erase(window_id);
+    pid_t plugin_pid = browser_needs_activation ? -1 : handle();
+    browser_needs_activation = false;
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        NewRunnableFunction(ReleasePluginFullScreen, plugin_pid));
+  }
+
+  if (browser_needs_activation) {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        NewRunnableFunction(base::mac::ActivateProcess,
+                            base::GetCurrentProcId()));
+  }
+}
+
+void PluginProcessHost::OnAppActivation() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  // If our plugin process has any modal windows up, we need to bring it forward
+  // so that they act more like an in-process modal window would.
+  if (!plugin_modal_windows_set_.empty()) {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        NewRunnableFunction(base::mac::ActivateProcess, handle()));
+  }
+}
+
+void PluginProcessHost::OnPluginSetCursorVisibility(bool visible) {
+  if (plugin_cursor_visible_ != visible) {
+    plugin_cursor_visible_ = visible;
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        NewRunnableFunction(base::mac::SetCursorVisibility,
+                            visible));
+  }
+}
diff --git a/content/browser/plugin_service.cc b/content/browser/plugin_service.cc
new file mode 100644
index 0000000..756dbac
--- /dev/null
+++ b/content/browser/plugin_service.cc
@@ -0,0 +1,584 @@
+// 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/plugin_service.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/threading/thread.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/synchronization/waitable_event.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_plugin_host.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/plugin_updater.h"
+#include "chrome/browser/ppapi_plugin_process_host.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_plugin_lib.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/default_plugin.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/gpu_plugin.h"
+#include "chrome/common/logging_chrome.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pepper_plugin_registry.h"
+#include "chrome/common/plugin_messages.h"
+#include "chrome/common/render_messages.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "webkit/plugins/npapi/plugin_constants_win.h"
+#include "webkit/plugins/npapi/plugin_list.h"
+#include "webkit/plugins/npapi/webplugininfo.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/plugin_selection_policy.h"
+#endif
+
+#if defined(OS_MACOSX)
+static void NotifyPluginsOfActivation() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::PLUGIN_PROCESS);
+       !iter.Done(); ++iter) {
+    PluginProcessHost* plugin = static_cast<PluginProcessHost*>(*iter);
+    plugin->OnAppActivation();
+  }
+}
+#endif
+
+static void PurgePluginListCache(bool reload_pages) {
+  for (RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator();
+       !it.IsAtEnd(); it.Advance()) {
+    it.GetCurrentValue()->Send(new ViewMsg_PurgePluginListCache(reload_pages));
+  }
+}
+
+#if defined(OS_LINUX)
+// Delegate class for monitoring directories.
+class PluginDirWatcherDelegate : public FilePathWatcher::Delegate {
+  virtual void OnFilePathChanged(const FilePath& path) {
+    VLOG(1) << "Watched path changed: " << path.value();
+    // Make the plugin list update itself
+    webkit::npapi::PluginList::Singleton()->RefreshPlugins();
+  }
+  virtual void OnError() {
+    // TODO(pastarmovj): Add some sensible error handling. Maybe silently
+    // stopping the watcher would be enough. Or possibly restart it.
+    NOTREACHED();
+  }
+};
+#endif
+
+// static
+bool PluginService::enable_chrome_plugins_ = true;
+
+// static
+void PluginService::InitGlobalInstance(Profile* profile) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  // We first group the plugins and then figure out which groups to disable.
+  PluginUpdater::GetInstance()->DisablePluginGroupsFromPrefs(profile);
+
+  // Have Chrome plugins write their data to the profile directory.
+  GetInstance()->SetChromePluginDataDir(profile->GetPath());
+}
+
+// static
+PluginService* PluginService::GetInstance() {
+  return Singleton<PluginService>::get();
+}
+
+// static
+void PluginService::EnableChromePlugins(bool enable) {
+  enable_chrome_plugins_ = enable;
+}
+
+PluginService::PluginService()
+    : main_message_loop_(MessageLoop::current()),
+      resource_dispatcher_host_(NULL),
+      ui_locale_(g_browser_process->GetApplicationLocale()) {
+  RegisterPepperPlugins();
+
+  // Have the NPAPI plugin list search for Chrome plugins as well.
+  ChromePluginLib::RegisterPluginsWithNPAPI();
+
+  // Load any specified on the command line as well.
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+  FilePath path = command_line->GetSwitchValuePath(switches::kLoadPlugin);
+  if (!path.empty())
+    webkit::npapi::PluginList::Singleton()->AddExtraPluginPath(path);
+  path = command_line->GetSwitchValuePath(switches::kExtraPluginDir);
+  if (!path.empty())
+    webkit::npapi::PluginList::Singleton()->AddExtraPluginDir(path);
+
+  chrome::RegisterInternalDefaultPlugin();
+
+  // Register the internal Flash and PDF, if available.
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableInternalFlash) &&
+      PathService::Get(chrome::FILE_FLASH_PLUGIN, &path)) {
+    webkit::npapi::PluginList::Singleton()->AddExtraPluginPath(path);
+  }
+
+#if defined(OS_CHROMEOS)
+  plugin_selection_policy_ = new chromeos::PluginSelectionPolicy;
+  plugin_selection_policy_->StartInit();
+#endif
+
+  chrome::RegisterInternalGPUPlugin();
+
+  // Start watching for changes in the plugin list. This means watching
+  // for changes in the Windows registry keys and on both Windows and POSIX
+  // watch for changes in the paths that are expected to contain plugins.
+#if defined(OS_WIN)
+  hkcu_key_.Create(
+      HKEY_CURRENT_USER, webkit::npapi::kRegistryMozillaPlugins, KEY_NOTIFY);
+  hklm_key_.Create(
+      HKEY_LOCAL_MACHINE, webkit::npapi::kRegistryMozillaPlugins, KEY_NOTIFY);
+  if (hkcu_key_.StartWatching() == ERROR_SUCCESS) {
+    hkcu_event_.reset(new base::WaitableEvent(hkcu_key_.watch_event()));
+    hkcu_watcher_.StartWatching(hkcu_event_.get(), this);
+  }
+
+  if (hklm_key_.StartWatching() == ERROR_SUCCESS) {
+    hklm_event_.reset(new base::WaitableEvent(hklm_key_.watch_event()));
+    hklm_watcher_.StartWatching(hklm_event_.get(), this);
+  }
+#elif defined(OS_POSIX) && !defined(OS_MACOSX)
+  // Also find plugins in a user-specific plugins dir,
+  // e.g. ~/.config/chromium/Plugins.
+  FilePath user_data_dir;
+  if (PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
+    webkit::npapi::PluginList::Singleton()->AddExtraPluginDir(
+        user_data_dir.Append("Plugins"));
+  }
+#endif
+// The FilePathWatcher produces too many false positives on MacOS (access time
+// updates?) which will lead to enforcing updates of the plugins way too often.
+// On ChromeOS the user can't install plugins anyway and on Windows all
+// important plugins register themselves in the registry so no need to do that.
+#if defined(OS_LINUX)
+  file_watcher_delegate_ = new PluginDirWatcherDelegate();
+  // Get the list of all paths for registering the FilePathWatchers
+  // that will track and if needed reload the list of plugins on runtime.
+  std::vector<FilePath> plugin_dirs;
+  webkit::npapi::PluginList::Singleton()->GetPluginDirectories(
+      &plugin_dirs);
+
+  for (size_t i = 0; i < plugin_dirs.size(); ++i) {
+    FilePathWatcher* watcher = new FilePathWatcher();
+    // FilePathWatcher can not handle non-absolute paths under windows.
+    // We don't watch for file changes in windows now but if this should ever
+    // be extended to Windows these lines might save some time of debugging.
+#if defined(OS_WIN)
+    if (!plugin_dirs[i].IsAbsolute())
+      continue;
+#endif
+    VLOG(1) << "Watching for changes in: " << plugin_dirs[i].value();
+    BrowserThread::PostTask(
+        BrowserThread::FILE, FROM_HERE,
+        NewRunnableFunction(
+            &PluginService::RegisterFilePathWatcher,
+            watcher, plugin_dirs[i], file_watcher_delegate_));
+    file_watchers_.push_back(watcher);
+  }
+#endif
+  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
+                 NotificationService::AllSources());
+  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
+                 NotificationService::AllSources());
+#if defined(OS_MACOSX)
+  // We need to know when the browser comes forward so we can bring modal plugin
+  // windows forward too.
+  registrar_.Add(this, NotificationType::APP_ACTIVATED,
+                 NotificationService::AllSources());
+#endif
+  registrar_.Add(this, NotificationType::PLUGIN_ENABLE_STATUS_CHANGED,
+                 NotificationService::AllSources());
+  registrar_.Add(this,
+                 NotificationType::RENDERER_PROCESS_CLOSED,
+                 NotificationService::AllSources());
+}
+
+PluginService::~PluginService() {
+#if defined(OS_WIN)
+  // Release the events since they're owned by RegKey, not WaitableEvent.
+  hkcu_watcher_.StopWatching();
+  hklm_watcher_.StopWatching();
+  if (hkcu_event_.get())
+    hkcu_event_->Release();
+  if (hklm_event_.get())
+    hklm_event_->Release();
+#endif
+}
+
+void PluginService::LoadChromePlugins(
+    ResourceDispatcherHost* resource_dispatcher_host) {
+  if (!enable_chrome_plugins_)
+    return;
+
+  resource_dispatcher_host_ = resource_dispatcher_host;
+  ChromePluginLib::LoadChromePlugins(GetCPBrowserFuncsForBrowser());
+}
+
+void PluginService::SetChromePluginDataDir(const FilePath& data_dir) {
+  chrome_plugin_data_dir_ = data_dir;
+}
+
+const FilePath& PluginService::GetChromePluginDataDir() {
+  return chrome_plugin_data_dir_;
+}
+
+const std::string& PluginService::GetUILocale() {
+  return ui_locale_;
+}
+
+PluginProcessHost* PluginService::FindNpapiPluginProcess(
+    const FilePath& plugin_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::PLUGIN_PROCESS);
+       !iter.Done(); ++iter) {
+    PluginProcessHost* plugin = static_cast<PluginProcessHost*>(*iter);
+    if (plugin->info().path == plugin_path)
+      return plugin;
+  }
+
+  return NULL;
+}
+
+PpapiPluginProcessHost* PluginService::FindPpapiPluginProcess(
+    const FilePath& plugin_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  for (BrowserChildProcessHost::Iterator iter(
+           ChildProcessInfo::PPAPI_PLUGIN_PROCESS);
+       !iter.Done(); ++iter) {
+    PpapiPluginProcessHost* plugin =
+        static_cast<PpapiPluginProcessHost*>(*iter);
+    if (plugin->plugin_path() == plugin_path)
+      return plugin;
+  }
+
+  return NULL;
+}
+
+PluginProcessHost* PluginService::FindOrStartNpapiPluginProcess(
+    const FilePath& plugin_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  PluginProcessHost* plugin_host = FindNpapiPluginProcess(plugin_path);
+  if (plugin_host)
+    return plugin_host;
+
+  webkit::npapi::WebPluginInfo info;
+  if (!webkit::npapi::PluginList::Singleton()->GetPluginInfoByPath(
+          plugin_path, &info)) {
+    return NULL;
+  }
+
+  // This plugin isn't loaded by any plugin process, so create a new process.
+  scoped_ptr<PluginProcessHost> new_host(new PluginProcessHost());
+  if (!new_host->Init(info, ui_locale_)) {
+    NOTREACHED();  // Init is not expected to fail.
+    return NULL;
+  }
+  return new_host.release();
+}
+
+PpapiPluginProcessHost* PluginService::FindOrStartPpapiPluginProcess(
+    const FilePath& plugin_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  PpapiPluginProcessHost* plugin_host = FindPpapiPluginProcess(plugin_path);
+  if (plugin_host)
+    return plugin_host;
+
+  // Validate that the plugin is actually registered. There should generally
+  // be very few plugins so a brute-force search is fine.
+  PepperPluginInfo* info = NULL;
+  for (size_t i = 0; i < ppapi_plugins_.size(); i++) {
+    if (ppapi_plugins_[i].path == plugin_path) {
+      info = &ppapi_plugins_[i];
+      break;
+    }
+  }
+  if (!info)
+    return NULL;
+
+  // This plugin isn't loaded by any plugin process, so create a new process.
+  scoped_ptr<PpapiPluginProcessHost> new_host(new PpapiPluginProcessHost);
+  if (!new_host->Init(plugin_path)) {
+    NOTREACHED();  // Init is not expected to fail.
+    return NULL;
+  }
+  return new_host.release();
+}
+
+void PluginService::OpenChannelToNpapiPlugin(
+    int render_process_id,
+    int render_view_id,
+    const GURL& url,
+    const std::string& mime_type,
+    PluginProcessHost::Client* client) {
+  // The PluginList::GetFirstAllowedPluginInfo may need to load the
+  // plugins.  Don't do it on the IO thread.
+  BrowserThread::PostTask(
+      BrowserThread::FILE, FROM_HERE,
+      NewRunnableMethod(
+          this, &PluginService::GetAllowedPluginForOpenChannelToPlugin,
+          render_process_id, render_view_id, url, mime_type, client));
+}
+
+void PluginService::OpenChannelToPpapiPlugin(
+    const FilePath& path,
+    PpapiPluginProcessHost::Client* client) {
+  PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess(path);
+  if (plugin_host)
+    plugin_host->OpenChannelToPlugin(client);
+  else  // Send error.
+    client->OnChannelOpened(base::kNullProcessHandle, IPC::ChannelHandle());
+}
+
+void PluginService::GetAllowedPluginForOpenChannelToPlugin(
+    int render_process_id,
+    int render_view_id,
+    const GURL& url,
+    const std::string& mime_type,
+    PluginProcessHost::Client* client) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  webkit::npapi::WebPluginInfo info;
+  bool found = GetFirstAllowedPluginInfo(
+      render_process_id, render_view_id, url, mime_type, &info, NULL);
+  FilePath plugin_path;
+  if (found && webkit::npapi::IsPluginEnabled(info))
+    plugin_path = FilePath(info.path);
+
+  // Now we jump back to the IO thread to finish opening the channel.
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      NewRunnableMethod(
+          this, &PluginService::FinishOpenChannelToPlugin,
+          plugin_path, client));
+}
+
+void PluginService::FinishOpenChannelToPlugin(
+    const FilePath& plugin_path,
+    PluginProcessHost::Client* client) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  PluginProcessHost* plugin_host = FindOrStartNpapiPluginProcess(plugin_path);
+  if (plugin_host)
+    plugin_host->OpenChannelToPlugin(client);
+  else
+    client->OnError();
+}
+
+bool PluginService::GetFirstAllowedPluginInfo(
+    int render_process_id,
+    int render_view_id,
+    const GURL& url,
+    const std::string& mime_type,
+    webkit::npapi::WebPluginInfo* info,
+    std::string* actual_mime_type) {
+  // GetPluginInfoArray may need to load the plugins, so we need to be
+  // on the FILE thread.
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  bool allow_wildcard = true;
+#if defined(OS_CHROMEOS)
+  std::vector<webkit::npapi::WebPluginInfo> info_array;
+  std::vector<std::string> actual_mime_types;
+  webkit::npapi::PluginList::Singleton()->GetPluginInfoArray(
+      url, mime_type, allow_wildcard, &info_array, &actual_mime_types);
+
+  // Now we filter by the plugin selection policy.
+  int allowed_index = plugin_selection_policy_->FindFirstAllowed(url,
+                                                                 info_array);
+  if (!info_array.empty() && allowed_index >= 0) {
+    *info = info_array[allowed_index];
+    if (actual_mime_type)
+      *actual_mime_type = actual_mime_types[allowed_index];
+    return true;
+  }
+  return false;
+#else
+  {
+    base::AutoLock auto_lock(overridden_plugins_lock_);
+    for (size_t i = 0; i < overridden_plugins_.size(); ++i) {
+      if (overridden_plugins_[i].render_process_id == render_process_id &&
+          overridden_plugins_[i].render_view_id == render_view_id &&
+          overridden_plugins_[i].url == url) {
+        if (actual_mime_type)
+          *actual_mime_type = mime_type;
+        *info = overridden_plugins_[i].plugin;
+        return true;
+      }
+    }
+  }
+  return webkit::npapi::PluginList::Singleton()->GetPluginInfo(
+      url, mime_type, allow_wildcard, info, actual_mime_type);
+#endif
+}
+
+void PluginService::OnWaitableEventSignaled(
+    base::WaitableEvent* waitable_event) {
+#if defined(OS_WIN)
+  if (waitable_event == hkcu_event_.get()) {
+    hkcu_key_.StartWatching();
+  } else {
+    hklm_key_.StartWatching();
+  }
+
+  webkit::npapi::PluginList::Singleton()->RefreshPlugins();
+  PurgePluginListCache(true);
+#else
+  // This event should only get signaled on a Windows machine.
+  NOTREACHED();
+#endif  // defined(OS_WIN)
+}
+
+static void ForceShutdownPlugin(const FilePath& plugin_path) {
+  PluginProcessHost* plugin =
+      PluginService::GetInstance()->FindNpapiPluginProcess(plugin_path);
+  if (plugin)
+    plugin->ForceShutdown();
+}
+
+void PluginService::Observe(NotificationType type,
+                            const NotificationSource& source,
+                            const NotificationDetails& details) {
+  switch (type.value) {
+    case NotificationType::EXTENSION_LOADED: {
+      const Extension* extension = Details<const Extension>(details).ptr();
+      bool plugins_changed = false;
+      for (size_t i = 0; i < extension->plugins().size(); ++i) {
+        const Extension::PluginInfo& plugin = extension->plugins()[i];
+        webkit::npapi::PluginList::Singleton()->RefreshPlugins();
+        webkit::npapi::PluginList::Singleton()->AddExtraPluginPath(plugin.path);
+        plugins_changed = true;
+        if (!plugin.is_public)
+          private_plugins_[plugin.path] = extension->url();
+      }
+      if (plugins_changed)
+        PurgePluginListCache(false);
+      break;
+    }
+
+    case NotificationType::EXTENSION_UNLOADED: {
+      const Extension* extension =
+          Details<UnloadedExtensionInfo>(details)->extension;
+      bool plugins_changed = false;
+      for (size_t i = 0; i < extension->plugins().size(); ++i) {
+        const Extension::PluginInfo& plugin = extension->plugins()[i];
+        BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                                NewRunnableFunction(&ForceShutdownPlugin,
+                                                    plugin.path));
+        webkit::npapi::PluginList::Singleton()->RefreshPlugins();
+        webkit::npapi::PluginList::Singleton()->RemoveExtraPluginPath(
+            plugin.path);
+        plugins_changed = true;
+        if (!plugin.is_public)
+          private_plugins_.erase(plugin.path);
+      }
+      if (plugins_changed)
+        PurgePluginListCache(false);
+      break;
+    }
+
+#if defined(OS_MACOSX)
+    case NotificationType::APP_ACTIVATED: {
+      BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                              NewRunnableFunction(&NotifyPluginsOfActivation));
+      break;
+    }
+#endif
+
+    case NotificationType::PLUGIN_ENABLE_STATUS_CHANGED: {
+      webkit::npapi::PluginList::Singleton()->RefreshPlugins();
+      PurgePluginListCache(false);
+      break;
+    }
+    case NotificationType::RENDERER_PROCESS_CLOSED: {
+      int render_process_id = Source<RenderProcessHost>(source).ptr()->id();
+
+      base::AutoLock auto_lock(overridden_plugins_lock_);
+      for (size_t i = 0; i < overridden_plugins_.size(); ++i) {
+        if (overridden_plugins_[i].render_process_id == render_process_id) {
+          overridden_plugins_.erase(overridden_plugins_.begin() + i);
+          break;
+        }
+      }
+      break;
+    }
+    default:
+      NOTREACHED();
+  }
+}
+
+bool PluginService::PrivatePluginAllowedForURL(const FilePath& plugin_path,
+                                               const GURL& url) {
+  if (url.is_empty())
+    return true;  // Caller wants all plugins.
+
+  PrivatePluginMap::iterator it = private_plugins_.find(plugin_path);
+  if (it == private_plugins_.end())
+    return true;  // This plugin is not private, so it's allowed everywhere.
+
+  // We do a dumb compare of scheme and host, rather than using the domain
+  // service, since we only care about this for extensions.
+  const GURL& required_url = it->second;
+  return (url.scheme() == required_url.scheme() &&
+          url.host() == required_url.host());
+}
+
+void PluginService::OverridePluginForTab(OverriddenPlugin plugin) {
+  base::AutoLock auto_lock(overridden_plugins_lock_);
+  overridden_plugins_.push_back(plugin);
+}
+
+void PluginService::RegisterPepperPlugins() {
+  PepperPluginRegistry::ComputeList(&ppapi_plugins_);
+  for (size_t i = 0; i < ppapi_plugins_.size(); ++i) {
+    webkit::npapi::WebPluginInfo info;
+    info.path = ppapi_plugins_[i].path;
+    info.name = ppapi_plugins_[i].name.empty() ?
+        ppapi_plugins_[i].path.BaseName().LossyDisplayName() :
+        ASCIIToUTF16(ppapi_plugins_[i].name);
+    info.desc = ASCIIToUTF16(ppapi_plugins_[i].description);
+    info.version = ASCIIToUTF16(ppapi_plugins_[i].version);
+    info.enabled = webkit::npapi::WebPluginInfo::USER_ENABLED_POLICY_UNMANAGED;
+
+    // TODO(evan): Pepper shouldn't require us to parse strings to get
+    // the list of mime types out.
+    if (!webkit::npapi::PluginList::ParseMimeTypes(
+            JoinString(ppapi_plugins_[i].mime_types, '|'),
+            ppapi_plugins_[i].file_extensions,
+            ASCIIToUTF16(ppapi_plugins_[i].type_descriptions),
+            &info.mime_types)) {
+      LOG(ERROR) << "Error parsing mime types for "
+                 << ppapi_plugins_[i].path.LossyDisplayName();
+      return;
+    }
+
+    webkit::npapi::PluginList::Singleton()->RegisterInternalPlugin(info);
+  }
+}
+
+#if defined(OS_LINUX)
+// static
+void PluginService::RegisterFilePathWatcher(
+    FilePathWatcher *watcher,
+    const FilePath& path,
+    FilePathWatcher::Delegate* delegate) {
+  bool result = watcher->Watch(path, delegate);
+  DCHECK(result);
+}
+#endif
diff --git a/content/browser/plugin_service.h b/content/browser/plugin_service.h
new file mode 100644
index 0000000..f9bc49cd
--- /dev/null
+++ b/content/browser/plugin_service.h
@@ -0,0 +1,232 @@
+// 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.
+
+// This class responds to requests from renderers for the list of plugins, and
+// also a proxy object for plugin instances.
+
+#ifndef CONTENT_BROWSER_PLUGIN_SERVICE_H_
+#define CONTENT_BROWSER_PLUGIN_SERVICE_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/hash_tables.h"
+#include "base/scoped_vector.h"
+#include "base/singleton.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event_watcher.h"
+#include "build/build_config.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "content/browser/plugin_process_host.h"
+#include "content/browser/ppapi_plugin_process_host.h"
+#include "googleurl/src/gurl.h"
+#include "ipc/ipc_channel_handle.h"
+#include "webkit/plugins/npapi/webplugininfo.h"
+
+#if defined(OS_WIN)
+#include "base/scoped_ptr.h"
+#include "base/win/registry.h"
+#endif
+
+#if defined(OS_LINUX)
+#include "chrome/browser/file_path_watcher/file_path_watcher.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+namespace chromeos {
+class PluginSelectionPolicy;
+}
+#endif
+
+namespace IPC {
+class Message;
+}
+
+class MessageLoop;
+struct PepperPluginInfo;
+class PluginDirWatcherDelegate;
+class Profile;
+class ResourceDispatcherHost;
+
+namespace net {
+class URLRequestContext;
+}  // namespace net
+
+// This must be created on the main thread but it's only called on the IO/file
+// thread.
+class PluginService
+    : public base::WaitableEventWatcher::Delegate,
+      public NotificationObserver {
+ public:
+  struct OverriddenPlugin {
+    int render_process_id;
+    int render_view_id;
+    GURL url;
+    webkit::npapi::WebPluginInfo plugin;
+  };
+
+  // Initializes the global instance; should be called on startup from the main
+  // thread.
+  static void InitGlobalInstance(Profile* profile);
+
+  // Returns the PluginService singleton.
+  static PluginService* GetInstance();
+
+  // Load all the plugins that should be loaded for the lifetime of the browser
+  // (ie, with the LoadOnStartup flag set).
+  void LoadChromePlugins(ResourceDispatcherHost* resource_dispatcher_host);
+
+  // Sets/gets the data directory that Chrome plugins should use to store
+  // persistent data.
+  void SetChromePluginDataDir(const FilePath& data_dir);
+  const FilePath& GetChromePluginDataDir();
+
+  // Gets the browser's UI locale.
+  const std::string& GetUILocale();
+
+  // Returns the plugin process host corresponding to the plugin process that
+  // has been started by this service. Returns NULL if no process has been
+  // started.
+  PluginProcessHost* FindNpapiPluginProcess(const FilePath& plugin_path);
+  PpapiPluginProcessHost* FindPpapiPluginProcess(const FilePath& plugin_path);
+
+  // Returns the plugin process host corresponding to the plugin process that
+  // has been started by this service. This will start a process to host the
+  // 'plugin_path' if needed. If the process fails to start, the return value
+  // is NULL. Must be called on the IO thread.
+  PluginProcessHost* FindOrStartNpapiPluginProcess(
+      const FilePath& plugin_path);
+  PpapiPluginProcessHost* FindOrStartPpapiPluginProcess(
+      const FilePath& plugin_path);
+
+  // Opens a channel to a plugin process for the given mime type, starting
+  // a new plugin process if necessary.  This must be called on the IO thread
+  // or else a deadlock can occur.
+  void OpenChannelToNpapiPlugin(int render_process_id,
+                                int render_view_id,
+                                const GURL& url,
+                                const std::string& mime_type,
+                                PluginProcessHost::Client* client);
+  void OpenChannelToPpapiPlugin(const FilePath& path,
+                                PpapiPluginProcessHost::Client* client);
+
+  // Gets the first allowed plugin in the list of plugins that matches
+  // the given url and mime type.  Must be called on the FILE thread.
+  bool GetFirstAllowedPluginInfo(int render_process_id,
+                                 int render_view_id,
+                                 const GURL& url,
+                                 const std::string& mime_type,
+                                 webkit::npapi::WebPluginInfo* info,
+                                 std::string* actual_mime_type);
+
+  // Returns true if the given plugin is allowed to be used by a page with
+  // the given URL.
+  bool PrivatePluginAllowedForURL(const FilePath& plugin_path, const GURL& url);
+
+  // Safe to be called from any thread.
+  void OverridePluginForTab(OverriddenPlugin plugin);
+
+  // The UI thread's message loop
+  MessageLoop* main_message_loop() { return main_message_loop_; }
+
+  ResourceDispatcherHost* resource_dispatcher_host() const {
+    return resource_dispatcher_host_;
+  }
+
+  static void EnableChromePlugins(bool enable);
+
+ private:
+  friend struct DefaultSingletonTraits<PluginService>;
+
+  // Creates the PluginService object, but doesn't actually build the plugin
+  // list yet.  It's generated lazily.
+  PluginService();
+  ~PluginService();
+
+  // base::WaitableEventWatcher::Delegate implementation.
+  virtual void OnWaitableEventSignaled(base::WaitableEvent* waitable_event);
+
+  // NotificationObserver implementation
+  virtual void Observe(NotificationType type, const NotificationSource& source,
+                       const NotificationDetails& details);
+
+  void RegisterPepperPlugins();
+
+  // Helper so we can do the plugin lookup on the FILE thread.
+  void GetAllowedPluginForOpenChannelToPlugin(
+      int render_process_id,
+      int render_view_id,
+      const GURL& url,
+      const std::string& mime_type,
+      PluginProcessHost::Client* client);
+
+  // Helper so we can finish opening the channel after looking up the
+  // plugin.
+  void FinishOpenChannelToPlugin(
+      const FilePath& plugin_path,
+      PluginProcessHost::Client* client);
+
+#if defined(OS_LINUX)
+  // Registers a new FilePathWatcher for a given path.
+  static void RegisterFilePathWatcher(
+      FilePathWatcher* watcher,
+      const FilePath& path,
+      FilePathWatcher::Delegate* delegate);
+#endif
+
+  // The main thread's message loop.
+  MessageLoop* main_message_loop_;
+
+  // The IO thread's resource dispatcher host.
+  ResourceDispatcherHost* resource_dispatcher_host_;
+
+  // The data directory that Chrome plugins should use to store persistent data.
+  FilePath chrome_plugin_data_dir_;
+
+  // The browser's UI locale.
+  const std::string ui_locale_;
+
+  // Map of plugin paths to the origin they are restricted to.  Used for
+  // extension-only plugins.
+  typedef base::hash_map<FilePath, GURL> PrivatePluginMap;
+  PrivatePluginMap private_plugins_;
+
+  NotificationRegistrar registrar_;
+
+#if defined(OS_CHROMEOS)
+  scoped_refptr<chromeos::PluginSelectionPolicy> plugin_selection_policy_;
+#endif
+
+#if defined(OS_WIN)
+  // Registry keys for getting notifications when new plugins are installed.
+  base::win::RegKey hkcu_key_;
+  base::win::RegKey hklm_key_;
+  scoped_ptr<base::WaitableEvent> hkcu_event_;
+  scoped_ptr<base::WaitableEvent> hklm_event_;
+  base::WaitableEventWatcher hkcu_watcher_;
+  base::WaitableEventWatcher hklm_watcher_;
+#endif
+
+#if defined(OS_LINUX)
+  ScopedVector<FilePathWatcher> file_watchers_;
+  scoped_refptr<PluginDirWatcherDelegate> file_watcher_delegate_;
+#endif
+
+  std::vector<PepperPluginInfo> ppapi_plugins_;
+
+  // Set to true if chrome plugins are enabled. Defaults to true.
+  static bool enable_chrome_plugins_;
+
+  std::vector<OverriddenPlugin> overridden_plugins_;
+  base::Lock overridden_plugins_lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(PluginService);
+};
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(PluginService);
+
+#endif  // CONTENT_BROWSER_PLUGIN_SERVICE_H_
diff --git a/content/browser/plugin_service_browsertest.cc b/content/browser/plugin_service_browsertest.cc
new file mode 100644
index 0000000..2a0edb5
--- /dev/null
+++ b/content/browser/plugin_service_browsertest.cc
@@ -0,0 +1,107 @@
+// 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/plugin_service.h"
+
+#include "base/auto_reset.h"
+#include "base/command_line.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/testing_profile.h"
+#include "content/browser/browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "webkit/plugins/npapi/plugin_list.h"
+
+namespace {
+
+// We have to mock the Client class up in order to be able to test the
+// OpenChannelToPlugin function. The only really needed function of this mockup
+// is SetPluginInfo, which gets called in
+// PluginService::FinishOpenChannelToPlugin.
+class MockPluginProcessHostClient : public PluginProcessHost::Client {
+ public:
+  MockPluginProcessHostClient() {}
+  virtual ~MockPluginProcessHostClient() {}
+
+  MOCK_METHOD0(ID, int());
+  MOCK_METHOD0(OffTheRecord, bool());
+  MOCK_METHOD1(SetPluginInfo, void(const webkit::npapi::WebPluginInfo& info));
+  MOCK_METHOD1(OnChannelOpened, void(const IPC::ChannelHandle& handle));
+  MOCK_METHOD0(OnError, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient);
+};
+
+class PluginServiceTest : public testing::Test {
+ public:
+  PluginServiceTest()
+      : message_loop_(MessageLoop::TYPE_IO),
+        ui_thread_(BrowserThread::UI, &message_loop_),
+        file_thread_(BrowserThread::FILE, &message_loop_),
+        io_thread_(BrowserThread::IO, &message_loop_) {}
+
+  virtual ~PluginServiceTest() {}
+
+  virtual void SetUp() {
+    profile_.reset(new TestingProfile());
+
+    PluginService::InitGlobalInstance(profile_.get());
+    plugin_service_ = PluginService::GetInstance();
+    ASSERT_TRUE(plugin_service_);
+  }
+
+ protected:
+  MessageLoop message_loop_;
+  PluginService* plugin_service_;
+
+ private:
+  BrowserThread ui_thread_;
+  BrowserThread file_thread_;
+  BrowserThread io_thread_;
+  scoped_ptr<TestingProfile> profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(PluginServiceTest);
+};
+
+// These tests need to be implemented as in process tests because on mac os the
+// plugin loading mechanism checks whether plugin paths are in the bundle path
+// and the test fails this check when run outside of the browser process.
+IN_PROC_BROWSER_TEST_F(PluginServiceTest, StartAndFindPluginProcess) {
+  // Try to load the default plugin and if this is successful consecutive
+  // calls to FindPluginProcess should return non-zero values.
+  PluginProcessHost* default_plugin_process_host =
+      plugin_service_->FindOrStartNpapiPluginProcess(
+          FilePath(webkit::npapi::kDefaultPluginLibraryName));
+
+  EXPECT_EQ(default_plugin_process_host,
+            plugin_service_->FindNpapiPluginProcess(
+                FilePath(webkit::npapi::kDefaultPluginLibraryName)));
+}
+
+IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToPlugin) {
+  MockPluginProcessHostClient mock_client;
+  EXPECT_CALL(mock_client, SetPluginInfo(testing::_)).Times(1);
+  plugin_service_->OpenChannelToNpapiPlugin(0, 0, GURL("http://google.com/"),
+                                            "audio/mp3", &mock_client);
+  message_loop_.RunAllPending();
+}
+
+IN_PROC_BROWSER_TEST_F(PluginServiceTest, GetFirstAllowedPluginInfo) {
+  // on ChromeOS the plugin policy gets loaded on the FILE thread and the
+  // GetFirstAllowedPluginInfo will fail if we don't allow it to finish.
+  message_loop_.RunAllPending();
+  // We should always get a positive response no matter whether we really have
+  // a plugin to support that particular mime type because the Default plugin
+  // supports all mime types.
+  webkit::npapi::WebPluginInfo plugin_info;
+  std::string plugin_mime_type;
+  plugin_service_->GetFirstAllowedPluginInfo(0, 0, GURL("http://google.com/"),
+                                             "application/pdf",
+                                             &plugin_info,
+                                             &plugin_mime_type);
+  EXPECT_EQ("application/pdf", plugin_mime_type);
+}
+
+}  // namespace
diff --git a/content/browser/plugin_service_unittest.cc b/content/browser/plugin_service_unittest.cc
new file mode 100644
index 0000000..0fbde87
--- /dev/null
+++ b/content/browser/plugin_service_unittest.cc
@@ -0,0 +1,62 @@
+// 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/plugin_service.h"
+
+#include "base/auto_reset.h"
+#include "base/command_line.h"
+#include "chrome/test/testing_profile.h"
+#include "content/browser/browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class PluginServiceTest : public testing::Test {
+ public:
+  PluginServiceTest()
+      : message_loop_(MessageLoop::TYPE_IO),
+        ui_thread_(BrowserThread::UI, &message_loop_),
+        file_thread_(BrowserThread::FILE, &message_loop_),
+        io_thread_(BrowserThread::IO, &message_loop_) {}
+
+  virtual ~PluginServiceTest() {}
+
+  virtual void SetUp() {
+    profile_.reset(new TestingProfile());
+
+    PluginService::InitGlobalInstance(profile_.get());
+    plugin_service_ = PluginService::GetInstance();
+    ASSERT_TRUE(plugin_service_);
+  }
+
+ protected:
+  MessageLoop message_loop_;
+  PluginService* plugin_service_;
+
+ private:
+  BrowserThread ui_thread_;
+  BrowserThread file_thread_;
+  BrowserThread io_thread_;
+  scoped_ptr<TestingProfile> profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(PluginServiceTest);
+};
+
+TEST_F(PluginServiceTest, SetGetChromePluginDataDir) {
+  // Check that after setting the same plugin dir we just read it is set
+  // correctly.
+  FilePath plugin_data_dir = plugin_service_->GetChromePluginDataDir();
+  FilePath new_plugin_data_dir(FILE_PATH_LITERAL("/a/bogus/dir"));
+  plugin_service_->SetChromePluginDataDir(new_plugin_data_dir);
+  EXPECT_EQ(new_plugin_data_dir, plugin_service_->GetChromePluginDataDir());
+  plugin_service_->SetChromePluginDataDir(plugin_data_dir);
+  EXPECT_EQ(plugin_data_dir, plugin_service_->GetChromePluginDataDir());
+}
+
+TEST_F(PluginServiceTest, GetUILocale) {
+  // Check for a non-empty locale string.
+  EXPECT_NE("", plugin_service_->GetUILocale());
+}
+
+}  // namespace
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
new file mode 100644
index 0000000..def01b7d
--- /dev/null
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -0,0 +1,174 @@
+// 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/ppapi_plugin_process_host.h"
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/process_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/render_messages.h"
+#include "content/browser/plugin_service.h"
+#include "content/browser/renderer_host/render_message_filter.h"
+#include "ipc/ipc_switches.h"
+#include "ppapi/proxy/ppapi_messages.h"
+
+PpapiPluginProcessHost::PpapiPluginProcessHost()
+    : BrowserChildProcessHost(
+          ChildProcessInfo::PPAPI_PLUGIN_PROCESS,
+          PluginService::GetInstance()->resource_dispatcher_host()) {
+}
+
+PpapiPluginProcessHost::~PpapiPluginProcessHost() {
+  CancelRequests();
+}
+
+bool PpapiPluginProcessHost::Init(const FilePath& path) {
+  plugin_path_ = path;
+
+  if (!CreateChannel())
+    return false;
+
+  const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+  CommandLine::StringType plugin_launcher =
+      browser_command_line.GetSwitchValueNative(switches::kPpapiPluginLauncher);
+
+  FilePath exe_path = ChildProcessHost::GetChildPath(plugin_launcher.empty());
+  if (exe_path.empty())
+    return false;
+
+  CommandLine* cmd_line = new CommandLine(exe_path);
+  cmd_line->AppendSwitchASCII(switches::kProcessType,
+                              switches::kPpapiPluginProcess);
+  cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id());
+
+  if (!plugin_launcher.empty())
+    cmd_line->PrependWrapper(plugin_launcher);
+
+  // On posix, having a plugin launcher means we need to use another process
+  // instead of just forking the zygote.
+  Launch(
+#if defined(OS_WIN)
+      FilePath(),
+#elif defined(OS_POSIX)
+      plugin_launcher.empty(),
+      base::environment_vector(),
+#endif
+      cmd_line);
+  return true;
+}
+
+void PpapiPluginProcessHost::OpenChannelToPlugin(Client* client) {
+  if (opening_channel()) {
+    // The channel is already in the process of being opened.  Put
+    // this "open channel" request into a queue of requests that will
+    // be run once the channel is open.
+    pending_requests_.push_back(client);
+    return;
+  }
+
+  // We already have an open channel, send a request right away to plugin.
+  RequestPluginChannel(client);
+}
+
+void PpapiPluginProcessHost::RequestPluginChannel(Client* client) {
+  base::ProcessHandle process_handle;
+  int renderer_id;
+  client->GetChannelInfo(&process_handle, &renderer_id);
+
+  // We can't send any sync messages from the browser because it might lead to
+  // a hang. See the similar code in PluginProcessHost for more description.
+  PpapiMsg_CreateChannel* msg = new PpapiMsg_CreateChannel(process_handle,
+                                                           renderer_id);
+  msg->set_unblock(true);
+  if (Send(msg))
+    sent_requests_.push(client);
+  else
+    client->OnChannelOpened(base::kNullProcessHandle, IPC::ChannelHandle());
+}
+
+bool PpapiPluginProcessHost::CanShutdown() {
+  return true;
+}
+
+void PpapiPluginProcessHost::OnProcessLaunched() {
+}
+
+bool PpapiPluginProcessHost::OnMessageReceived(const IPC::Message& msg) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(PpapiPluginProcessHost, msg)
+    IPC_MESSAGE_HANDLER(PpapiHostMsg_ChannelCreated,
+                        OnRendererPluginChannelCreated)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  DCHECK(handled);
+  return handled;
+}
+
+// Called when the browser <--> plugin channel has been established.
+void PpapiPluginProcessHost::OnChannelConnected(int32 peer_pid) {
+  // This will actually load the plugin. Errors will actually not be reported
+  // back at this point. Instead, the plugin will fail to establish the
+  // connections when we request them on behalf of the renderer(s).
+  Send(new PpapiMsg_LoadPlugin(plugin_path_));
+
+  // Process all pending channel requests from the renderers.
+  for (size_t i = 0; i < pending_requests_.size(); i++)
+    RequestPluginChannel(pending_requests_[i]);
+  pending_requests_.clear();
+}
+
+// Called when the browser <--> plugin channel has an error. This normally
+// means the plugin has crashed.
+void PpapiPluginProcessHost::OnChannelError() {
+  // We don't need to notify the renderers that were communicating with the
+  // plugin since they have their own channels which will go into the error
+  // state at the same time. Instead, we just need to notify any renderers
+  // that have requested a connection but have not yet received one.
+  CancelRequests();
+}
+
+void PpapiPluginProcessHost::CancelRequests() {
+  for (size_t i = 0; i < pending_requests_.size(); i++) {
+    pending_requests_[i]->OnChannelOpened(base::kNullProcessHandle,
+                                          IPC::ChannelHandle());
+  }
+  pending_requests_.clear();
+
+  while (!sent_requests_.empty()) {
+    sent_requests_.front()->OnChannelOpened(base::kNullProcessHandle,
+                                            IPC::ChannelHandle());
+    sent_requests_.pop();
+  }
+}
+
+// Called when a new plugin <--> renderer channel has been created.
+void PpapiPluginProcessHost::OnRendererPluginChannelCreated(
+    const IPC::ChannelHandle& channel_handle) {
+  if (sent_requests_.empty())
+    return;
+
+  // All requests should be processed FIFO, so the next item in the
+  // sent_requests_ queue should be the one that the plugin just created.
+  Client* client = sent_requests_.front();
+  sent_requests_.pop();
+
+  // Prepare the handle to send to the renderer.
+  base::ProcessHandle plugin_process = GetChildProcessHandle();
+#if defined(OS_WIN)
+  base::ProcessHandle renderer_process;
+  int renderer_id;
+  client->GetChannelInfo(&renderer_process, &renderer_id);
+
+  base::ProcessHandle renderers_plugin_handle = NULL;
+  ::DuplicateHandle(::GetCurrentProcess(), plugin_process,
+                    renderer_process, &renderers_plugin_handle,
+                    0, FALSE, DUPLICATE_SAME_ACCESS);
+#elif defined(OS_POSIX)
+  // Don't need to duplicate anything on POSIX since it's just a PID.
+  base::ProcessHandle renderers_plugin_handle = plugin_process;
+#endif
+
+  client->OnChannelOpened(renderers_plugin_handle, channel_handle);
+}
diff --git a/content/browser/ppapi_plugin_process_host.h b/content/browser/ppapi_plugin_process_host.h
new file mode 100644
index 0000000..baeaf3b
--- /dev/null
+++ b/content/browser/ppapi_plugin_process_host.h
@@ -0,0 +1,78 @@
+// 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_PPAPI_PLUGIN_PROCESS_HOST_H_
+#define CONTENT_BROWSER_PPAPI_PLUGIN_PROCESS_HOST_H_
+#pragma once
+
+#include <queue>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "content/browser/browser_child_process_host.h"
+
+class PpapiPluginProcessHost : public BrowserChildProcessHost {
+ public:
+  class Client {
+   public:
+    // Gets the information about the renderer that's requesting the channel.
+    virtual void GetChannelInfo(base::ProcessHandle* renderer_handle,
+                                int* renderer_id) = 0;
+
+    // Called when the channel is asynchronously opened to the plugin or on
+    // error. On error, the parameters should be:
+    //   base::kNullProcessHandle
+    //   IPC::ChannelHandle()
+    virtual void OnChannelOpened(base::ProcessHandle plugin_process_handle,
+                                 const IPC::ChannelHandle& channel_handle) = 0;
+  };
+
+  // You must call init before doing anything else.
+  explicit PpapiPluginProcessHost();
+  virtual ~PpapiPluginProcessHost();
+
+  // Actually launches the process with the given plugin path. Returns true
+  // on success (the process was spawned).
+  bool Init(const FilePath& path);
+
+  // Opens a new channel to the plugin. The client will be notified when the
+  // channel is ready or if there's an error.
+  void OpenChannelToPlugin(Client* client);
+
+  const FilePath& plugin_path() const { return plugin_path_; }
+
+  // The client pointer must remain valid until its callback is issued.
+
+ private:
+
+  void RequestPluginChannel(Client* client);
+
+  virtual bool CanShutdown();
+  virtual void OnProcessLaunched();
+
+  virtual bool OnMessageReceived(const IPC::Message& msg);
+  virtual void OnChannelConnected(int32 peer_pid);
+  virtual void OnChannelError();
+
+  void CancelRequests();
+
+  // IPC message handlers.
+  void OnRendererPluginChannelCreated(const IPC::ChannelHandle& handle);
+
+  // Channel requests that we are waiting to send to the plugin process once
+  // the channel is opened.
+  std::vector<Client*> pending_requests_;
+
+  // Channel requests that we have already sent to the plugin process, but
+  // haven't heard back about yet.
+  std::queue<Client*> sent_requests_;
+
+  // Path to the plugin library.
+  FilePath plugin_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(PpapiPluginProcessHost);
+};
+
+#endif  // CONTENT_BROWSER_PPAPI_PLUGIN_PROCESS_HOST_H_
+
diff --git a/content/browser/worker.sb b/content/browser/worker.sb
new file mode 100644
index 0000000..c984670
--- /dev/null
+++ b/content/browser/worker.sb
@@ -0,0 +1,12 @@
+;;
+;; 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.
+;;
+; This is the Sandbox configuration file used for safeguarding the worker
+; process which is used to run web workers in a sandboxed environment.
+;
+; This is the most restrictive sandbox profile and only enables just enough
+; to allow basic use of Cocoa.
+
+; *** The contents of chrome/common/common.sb are implicitly included here. ***
\ No newline at end of file
diff --git a/content/browser/zygote_host_linux.cc b/content/browser/zygote_host_linux.cc
new file mode 100644
index 0000000..3b6f1fb
--- /dev/null
+++ b/content/browser/zygote_host_linux.cc
@@ -0,0 +1,362 @@
+// 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/zygote_host_linux.h"
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
+#include "base/eintr_wrapper.h"
+#include "base/environment.h"
+#include "base/linux_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/pickle.h"
+#include "base/process_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/process_watcher.h"
+#include "chrome/common/result_codes.h"
+#include "chrome/common/unix_domain_socket_posix.h"
+#include "content/browser/renderer_host/render_sandbox_host_linux.h"
+#include "sandbox/linux/suid/suid_unsafe_environment_variables.h"
+
+static void SaveSUIDUnsafeEnvironmentVariables() {
+  // The ELF loader will clear many environment variables so we save them to
+  // different names here so that the SUID sandbox can resolve them for the
+  // renderer.
+
+  for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
+    const char* const envvar = kSUIDUnsafeEnvironmentVariables[i];
+    char* const saved_envvar = SandboxSavedEnvironmentVariable(envvar);
+    if (!saved_envvar)
+      continue;
+
+    scoped_ptr<base::Environment> env(base::Environment::Create());
+    std::string value;
+    if (env->GetVar(envvar, &value))
+      env->SetVar(saved_envvar, value);
+    else
+      env->UnSetVar(saved_envvar);
+
+    free(saved_envvar);
+  }
+}
+
+ZygoteHost::ZygoteHost()
+    : control_fd_(-1),
+      pid_(-1),
+      init_(false),
+      using_suid_sandbox_(false),
+      have_read_sandbox_status_word_(false),
+      sandbox_status_(0) {
+}
+
+ZygoteHost::~ZygoteHost() {
+  if (init_)
+    close(control_fd_);
+}
+
+// static
+ZygoteHost* ZygoteHost::GetInstance() {
+  return Singleton<ZygoteHost>::get();
+}
+
+void ZygoteHost::Init(const std::string& sandbox_cmd) {
+  DCHECK(!init_);
+  init_ = true;
+
+  FilePath chrome_path;
+  CHECK(PathService::Get(base::FILE_EXE, &chrome_path));
+  CommandLine cmd_line(chrome_path);
+
+  cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess);
+
+  int fds[2];
+  CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
+  base::file_handle_mapping_vector fds_to_map;
+  fds_to_map.push_back(std::make_pair(fds[1], 3));
+
+  const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+  if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) {
+    cmd_line.PrependWrapper(
+        browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix));
+  }
+  // Append any switches from the browser process that need to be forwarded on
+  // to the zygote/renderers.
+  // Should this list be obtained from browser_render_process_host.cc?
+  static const char* kForwardSwitches[] = {
+    switches::kAllowSandboxDebugging,
+    switches::kLoggingLevel,
+    switches::kEnableLogging,  // Support, e.g., --enable-logging=stderr.
+    switches::kV,
+    switches::kVModule,
+    switches::kUserDataDir,  // Make logs go to the right file.
+    // Load (in-process) Pepper plugins in-process in the zygote pre-sandbox.
+    switches::kRegisterPepperPlugins,
+    switches::kDisableSeccompSandbox,
+    switches::kEnableSeccompSandbox,
+  };
+  cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches,
+                            arraysize(kForwardSwitches));
+
+  sandbox_binary_ = sandbox_cmd.c_str();
+  struct stat st;
+
+  if (!sandbox_cmd.empty() && stat(sandbox_binary_.c_str(), &st) == 0) {
+    if (access(sandbox_binary_.c_str(), X_OK) == 0 &&
+        (st.st_uid == 0) &&
+        (st.st_mode & S_ISUID) &&
+        (st.st_mode & S_IXOTH)) {
+      using_suid_sandbox_ = true;
+      cmd_line.PrependWrapper(sandbox_binary_);
+
+      SaveSUIDUnsafeEnvironmentVariables();
+    } else {
+      LOG(FATAL) << "The SUID sandbox helper binary was found, but is not "
+                    "configured correctly. Rather than run without sandboxing "
+                    "I'm aborting now. You need to make sure that "
+                 << sandbox_binary_ << " is mode 4755 and owned by root.";
+    }
+  }
+
+  // Start up the sandbox host process and get the file descriptor for the
+  // renderers to talk to it.
+  const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
+  fds_to_map.push_back(std::make_pair(sfd, 5));
+
+  int dummy_fd = -1;
+  if (using_suid_sandbox_) {
+    dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+    CHECK(dummy_fd >= 0);
+    fds_to_map.push_back(std::make_pair(dummy_fd, 7));
+  }
+
+  base::ProcessHandle process;
+  base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
+  CHECK(process != -1) << "Failed to launch zygote process";
+
+  if (using_suid_sandbox_) {
+    // In the SUID sandbox, the real zygote is forked from the sandbox.
+    // We need to look for it.
+    // But first, wait for the zygote to tell us it's running.
+    // The sending code is in chrome/browser/zygote_main_linux.cc.
+    std::vector<int> fds_vec;
+    const int kExpectedLength = sizeof(kZygoteMagic);
+    char buf[kExpectedLength];
+    const ssize_t len = UnixDomainSocket::RecvMsg(fds[0], buf, sizeof(buf),
+                                                  &fds_vec);
+    CHECK(len == kExpectedLength) << "Incorrect zygote magic length";
+    CHECK(0 == strcmp(buf, kZygoteMagic)) << "Incorrect zygote magic";
+
+    std::string inode_output;
+    ino_t inode = 0;
+    // Figure out the inode for |dummy_fd|, close |dummy_fd| on our end,
+    // and find the zygote process holding |dummy_fd|.
+    if (base::FileDescriptorGetInode(&inode, dummy_fd)) {
+      close(dummy_fd);
+      std::vector<std::string> get_inode_cmdline;
+      get_inode_cmdline.push_back(sandbox_binary_);
+      get_inode_cmdline.push_back(base::kFindInodeSwitch);
+      get_inode_cmdline.push_back(base::Int64ToString(inode));
+      CommandLine get_inode_cmd(get_inode_cmdline);
+      if (base::GetAppOutput(get_inode_cmd, &inode_output)) {
+        base::StringToInt(inode_output, &pid_);
+      }
+    }
+    CHECK(pid_ > 0) << "Did not find zygote process (using sandbox binary "
+        << sandbox_binary_ << ")";
+
+    if (process != pid_) {
+      // Reap the sandbox.
+      ProcessWatcher::EnsureProcessGetsReaped(process);
+    }
+  } else {
+    // Not using the SUID sandbox.
+    pid_ = process;
+  }
+
+  close(fds[1]);
+  control_fd_ = fds[0];
+
+  Pickle pickle;
+  pickle.WriteInt(kCmdGetSandboxStatus);
+  std::vector<int> empty_fds;
+  if (!UnixDomainSocket::SendMsg(control_fd_, pickle.data(), pickle.size(),
+                                 empty_fds))
+    LOG(FATAL) << "Cannot communicate with zygote";
+  // We don't wait for the reply. We'll read it in ReadReply.
+}
+
+ssize_t ZygoteHost::ReadReply(void* buf, size_t buf_len) {
+  // At startup we send a kCmdGetSandboxStatus request to the zygote, but don't
+  // wait for the reply. Thus, the first time that we read from the zygote, we
+  // get the reply to that request.
+  if (!have_read_sandbox_status_word_) {
+    if (HANDLE_EINTR(read(control_fd_, &sandbox_status_,
+                          sizeof(sandbox_status_))) !=
+        sizeof(sandbox_status_)) {
+      return -1;
+    }
+    have_read_sandbox_status_word_ = true;
+  }
+
+  return HANDLE_EINTR(read(control_fd_, buf, buf_len));
+}
+
+pid_t ZygoteHost::ForkRenderer(
+    const std::vector<std::string>& argv,
+    const base::GlobalDescriptors::Mapping& mapping) {
+  DCHECK(init_);
+  Pickle pickle;
+
+  pickle.WriteInt(kCmdFork);
+  pickle.WriteInt(argv.size());
+  for (std::vector<std::string>::const_iterator
+       i = argv.begin(); i != argv.end(); ++i)
+    pickle.WriteString(*i);
+
+  pickle.WriteInt(mapping.size());
+
+  std::vector<int> fds;
+  for (base::GlobalDescriptors::Mapping::const_iterator
+       i = mapping.begin(); i != mapping.end(); ++i) {
+    pickle.WriteUInt32(i->first);
+    fds.push_back(i->second);
+  }
+
+  pid_t pid;
+  {
+    base::AutoLock lock(control_lock_);
+    if (!UnixDomainSocket::SendMsg(control_fd_, pickle.data(), pickle.size(),
+                                   fds))
+      return base::kNullProcessHandle;
+
+    if (ReadReply(&pid, sizeof(pid)) != sizeof(pid))
+      return base::kNullProcessHandle;
+    if (pid <= 0)
+      return base::kNullProcessHandle;
+  }
+
+  const int kRendererScore = 5;
+  AdjustRendererOOMScore(pid, kRendererScore);
+
+  return pid;
+}
+
+void ZygoteHost::AdjustRendererOOMScore(base::ProcessHandle pid, int score) {
+  // 1) You can't change the oom_adj of a non-dumpable process (EPERM) unless
+  //    you're root. Because of this, we can't set the oom_adj from the browser
+  //    process.
+  //
+  // 2) We can't set the oom_adj before entering the sandbox because the
+  //    zygote is in the sandbox and the zygote is as critical as the browser
+  //    process. Its oom_adj value shouldn't be changed.
+  //
+  // 3) A non-dumpable process can't even change its own oom_adj because it's
+  //    root owned 0644. The sandboxed processes don't even have /proc, but one
+  //    could imagine passing in a descriptor from outside.
+  //
+  // So, in the normal case, we use the SUID binary to change it for us.
+  // However, Fedora (and other SELinux systems) don't like us touching other
+  // process's oom_adj values
+  // (https://bugzilla.redhat.com/show_bug.cgi?id=581256).
+  //
+  // The offical way to get the SELinux mode is selinux_getenforcemode, but I
+  // don't want to add another library to the build as it's sure to cause
+  // problems with other, non-SELinux distros.
+  //
+  // So we just check for /selinux. This isn't foolproof, but it's not bad
+  // and it's easy.
+
+  static bool selinux;
+  static bool selinux_valid = false;
+
+  if (!selinux_valid) {
+    selinux = access("/selinux", X_OK) == 0;
+    selinux_valid = true;
+  }
+
+  if (using_suid_sandbox_ && !selinux) {
+    base::ProcessHandle sandbox_helper_process;
+    std::vector<std::string> adj_oom_score_cmdline;
+
+    adj_oom_score_cmdline.push_back(sandbox_binary_);
+    adj_oom_score_cmdline.push_back(base::kAdjustOOMScoreSwitch);
+    adj_oom_score_cmdline.push_back(base::Int64ToString(pid));
+    adj_oom_score_cmdline.push_back(base::IntToString(score));
+    CommandLine adj_oom_score_cmd(adj_oom_score_cmdline);
+    if (base::LaunchApp(adj_oom_score_cmd, false, true,
+                        &sandbox_helper_process)) {
+      ProcessWatcher::EnsureProcessGetsReaped(sandbox_helper_process);
+    }
+  } else if (!using_suid_sandbox_) {
+    if (!base::AdjustOOMScore(pid, score))
+      PLOG(ERROR) << "Failed to adjust OOM score of renderer with pid " << pid;
+  }
+}
+
+void ZygoteHost::EnsureProcessTerminated(pid_t process) {
+  DCHECK(init_);
+  Pickle pickle;
+
+  pickle.WriteInt(kCmdReap);
+  pickle.WriteInt(process);
+
+  if (HANDLE_EINTR(write(control_fd_, pickle.data(), pickle.size())) < 0)
+    PLOG(ERROR) << "write";
+}
+
+base::TerminationStatus ZygoteHost::GetTerminationStatus(
+    base::ProcessHandle handle,
+    int* exit_code) {
+  DCHECK(init_);
+  Pickle pickle;
+  pickle.WriteInt(kCmdGetTerminationStatus);
+  pickle.WriteInt(handle);
+
+  // Set this now to handle the early termination cases.
+  if (exit_code)
+    *exit_code = ResultCodes::NORMAL_EXIT;
+
+  static const unsigned kMaxMessageLength = 128;
+  char buf[kMaxMessageLength];
+  ssize_t len;
+  {
+    base::AutoLock lock(control_lock_);
+    if (HANDLE_EINTR(write(control_fd_, pickle.data(), pickle.size())) < 0)
+      PLOG(ERROR) << "write";
+
+    len = ReadReply(buf, sizeof(buf));
+  }
+
+  if (len == -1) {
+    LOG(WARNING) << "Error reading message from zygote: " << errno;
+    return base::TERMINATION_STATUS_NORMAL_TERMINATION;
+  } else if (len == 0) {
+    LOG(WARNING) << "Socket closed prematurely.";
+    return base::TERMINATION_STATUS_NORMAL_TERMINATION;
+  }
+
+  Pickle read_pickle(buf, len);
+  int status, tmp_exit_code;
+  void* iter = NULL;
+  if (!read_pickle.ReadInt(&iter, &status) ||
+      !read_pickle.ReadInt(&iter, &tmp_exit_code)) {
+    LOG(WARNING) << "Error parsing GetTerminationStatus response from zygote.";
+    return base::TERMINATION_STATUS_NORMAL_TERMINATION;
+  }
+
+  if (exit_code)
+    *exit_code = tmp_exit_code;
+
+  return static_cast<base::TerminationStatus>(status);
+}
diff --git a/content/browser/zygote_host_linux.h b/content/browser/zygote_host_linux.h
new file mode 100644
index 0000000..5ead5f59
--- /dev/null
+++ b/content/browser/zygote_host_linux.h
@@ -0,0 +1,98 @@
+// 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_ZYGOTE_HOST_LINUX_H_
+#define CONTENT_BROWSER_ZYGOTE_HOST_LINUX_H_
+#pragma once
+
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/global_descriptors_posix.h"
+#include "base/process.h"
+#include "base/process_util.h"
+#include "base/synchronization/lock.h"
+
+template<typename Type>
+struct DefaultSingletonTraits;
+
+static const char kZygoteMagic[] = "ZYGOTE_OK";
+
+// http://code.google.com/p/chromium/wiki/LinuxZygote
+
+// The zygote host is the interface, in the browser process, to the zygote
+// process.
+class ZygoteHost {
+ public:
+  // Returns the singleton instance.
+  static ZygoteHost* GetInstance();
+
+  void Init(const std::string& sandbox_cmd);
+
+  // Tries to start a renderer process.  Returns its pid on success, otherwise
+  // base::kNullProcessHandle;
+  pid_t ForkRenderer(const std::vector<std::string>& command_line,
+                     const base::GlobalDescriptors::Mapping& mapping);
+  void EnsureProcessTerminated(pid_t process);
+
+  // Get the termination status (and, optionally, the exit code) of
+  // the process. |exit_code| is set to the exit code of the child
+  // process. (|exit_code| may be NULL.)
+  base::TerminationStatus GetTerminationStatus(base::ProcessHandle handle,
+                                               int* exit_code);
+
+  // These are the command codes used on the wire between the browser and the
+  // zygote.
+  enum {
+    kCmdFork = 0,                  // Fork off a new renderer.
+    kCmdReap = 1,                  // Reap a renderer child.
+    kCmdGetTerminationStatus = 2,  // Check what happend to a child process.
+    kCmdGetSandboxStatus = 3,      // Read a bitmask of kSandbox*
+  };
+
+  // These form a bitmask which describes the conditions of the sandbox that
+  // the zygote finds itself in.
+  enum {
+    kSandboxSUID = 1 << 0,     // SUID sandbox active
+    kSandboxPIDNS = 1 << 1,    // SUID sandbox is using the PID namespace
+    kSandboxNetNS = 1 << 2,    // SUID sandbox is using the network namespace
+    kSandboxSeccomp = 1 << 3,  // seccomp sandbox active.
+  };
+
+  pid_t pid() const { return pid_; }
+
+  // Returns an int which is a bitmask of kSandbox* values. Only valid after
+  // the first render has been forked.
+  int sandbox_status() const {
+    if (have_read_sandbox_status_word_)
+      return sandbox_status_;
+    return 0;
+  }
+
+  // Adjust the OOM score of the given renderer's PID.
+  void AdjustRendererOOMScore(base::ProcessHandle process_handle, int score);
+
+ private:
+  friend struct DefaultSingletonTraits<ZygoteHost>;
+  ZygoteHost();
+  ~ZygoteHost();
+
+  ssize_t ReadReply(void* buf, size_t buflen);
+
+  int control_fd_;  // the socket to the zygote
+  // A lock protecting all communication with the zygote. This lock must be
+  // acquired before sending a command and released after the result has been
+  // received.
+  base::Lock control_lock_;
+  pid_t pid_;
+  bool init_;
+  bool using_suid_sandbox_;
+  std::string sandbox_binary_;
+  bool have_read_sandbox_status_word_;
+  int sandbox_status_;
+};
+
+#endif  // CONTENT_BROWSER_ZYGOTE_HOST_LINUX_H_
diff --git a/content/browser/zygote_main_linux.cc b/content/browser/zygote_main_linux.cc
new file mode 100644
index 0000000..188ad34
--- /dev/null
+++ b/content/browser/zygote_main_linux.cc
@@ -0,0 +1,753 @@
+// 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 <dlfcn.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/epoll.h>
+#include <sys/prctl.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if defined(CHROMIUM_SELINUX)
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#endif
+
+#include "content/browser/zygote_host_linux.h"
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
+#include "base/global_descriptors_posix.h"
+#include "base/hash_tables.h"
+#include "base/linux_util.h"
+#include "base/path_service.h"
+#include "base/pickle.h"
+#include "base/process_util.h"
+#include "base/rand_util.h"
+#include "base/scoped_ptr.h"
+#include "base/sys_info.h"
+#include "build/build_config.h"
+#include "chrome/common/chrome_descriptors.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/font_config_ipc_linux.h"
+#include "chrome/common/main_function_params.h"
+#include "chrome/common/pepper_plugin_registry.h"
+#include "chrome/common/process_watcher.h"
+#include "chrome/common/result_codes.h"
+#include "chrome/common/sandbox_methods_linux.h"
+#include "chrome/common/set_process_title.h"
+#include "chrome/common/unix_domain_socket_posix.h"
+#include "media/base/media.h"
+#include "seccompsandbox/sandbox.h"
+#include "skia/ext/SkFontHost_fontconfig_control.h"
+#include "unicode/timezone.h"
+
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(CHROMIUM_SELINUX) && \
+    !defined(__clang__)
+// The seccomp sandbox is enabled on all ia32 and x86-64 processor as long as
+// we aren't using SELinux or clang.
+#define SECCOMP_SANDBOX
+#endif
+
+// http://code.google.com/p/chromium/wiki/LinuxZygote
+
+static const int kBrowserDescriptor = 3;
+static const int kMagicSandboxIPCDescriptor = 5;
+static const int kZygoteIdDescriptor = 7;
+static bool g_suid_sandbox_active = false;
+#if defined(SECCOMP_SANDBOX)
+// |g_proc_fd| is used only by the seccomp sandbox.
+static int g_proc_fd = -1;
+#endif
+
+#if defined(CHROMIUM_SELINUX)
+static void SELinuxTransitionToTypeOrDie(const char* type) {
+  security_context_t security_context;
+  if (getcon(&security_context))
+    LOG(FATAL) << "Cannot get SELinux context";
+
+  context_t context = context_new(security_context);
+  context_type_set(context, type);
+  const int r = setcon(context_str(context));
+  context_free(context);
+  freecon(security_context);
+
+  if (r) {
+    LOG(FATAL) << "dynamic transition to type '" << type << "' failed. "
+                  "(this binary has been built with SELinux support, but maybe "
+                  "the policies haven't been loaded into the kernel?)";
+  }
+}
+#endif  // CHROMIUM_SELINUX
+
+// This is the object which implements the zygote. The ZygoteMain function,
+// which is called from ChromeMain, simply constructs one of these objects and
+// runs it.
+class Zygote {
+ public:
+  explicit Zygote(int sandbox_flags)
+      : sandbox_flags_(sandbox_flags) {
+  }
+
+  bool ProcessRequests() {
+    // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the
+    // browser on it.
+    // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel.
+    // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
+
+    // We need to accept SIGCHLD, even though our handler is a no-op because
+    // otherwise we cannot wait on children. (According to POSIX 2001.)
+    struct sigaction action;
+    memset(&action, 0, sizeof(action));
+    action.sa_handler = SIGCHLDHandler;
+    CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
+
+    if (g_suid_sandbox_active) {
+      // Let the ZygoteHost know we are ready to go.
+      // The receiving code is in chrome/browser/zygote_host_linux.cc.
+      std::vector<int> empty;
+      bool r = UnixDomainSocket::SendMsg(kBrowserDescriptor, kZygoteMagic,
+                                         sizeof(kZygoteMagic), empty);
+      CHECK(r) << "Sending zygote magic failed";
+    }
+
+    for (;;) {
+      // This function call can return multiple times, once per fork().
+      if (HandleRequestFromBrowser(kBrowserDescriptor))
+        return true;
+    }
+  }
+
+ private:
+  // See comment below, where sigaction is called.
+  static void SIGCHLDHandler(int signal) { }
+
+  // ---------------------------------------------------------------------------
+  // Requests from the browser...
+
+  // Read and process a request from the browser. Returns true if we are in a
+  // new process and thus need to unwind back into ChromeMain.
+  bool HandleRequestFromBrowser(int fd) {
+    std::vector<int> fds;
+    static const unsigned kMaxMessageLength = 1024;
+    char buf[kMaxMessageLength];
+    const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
+
+    if (len == 0 || (len == -1 && errno == ECONNRESET)) {
+      // EOF from the browser. We should die.
+      _exit(0);
+      return false;
+    }
+
+    if (len == -1) {
+      PLOG(ERROR) << "Error reading message from browser";
+      return false;
+    }
+
+    Pickle pickle(buf, len);
+    void* iter = NULL;
+
+    int kind;
+    if (pickle.ReadInt(&iter, &kind)) {
+      switch (kind) {
+        case ZygoteHost::kCmdFork:
+          // This function call can return multiple times, once per fork().
+          return HandleForkRequest(fd, pickle, iter, fds);
+        case ZygoteHost::kCmdReap:
+          if (!fds.empty())
+            break;
+          HandleReapRequest(fd, pickle, iter);
+          return false;
+        case ZygoteHost::kCmdGetTerminationStatus:
+          if (!fds.empty())
+            break;
+          HandleGetTerminationStatus(fd, pickle, iter);
+          return false;
+        case ZygoteHost::kCmdGetSandboxStatus:
+          HandleGetSandboxStatus(fd, pickle, iter);
+          return false;
+        default:
+          NOTREACHED();
+          break;
+      }
+    }
+
+    LOG(WARNING) << "Error parsing message from browser";
+    for (std::vector<int>::const_iterator
+         i = fds.begin(); i != fds.end(); ++i)
+      close(*i);
+    return false;
+  }
+
+  void HandleReapRequest(int fd, const Pickle& pickle, void* iter) {
+    base::ProcessId child;
+    base::ProcessId actual_child;
+
+    if (!pickle.ReadInt(&iter, &child)) {
+      LOG(WARNING) << "Error parsing reap request from browser";
+      return;
+    }
+
+    if (g_suid_sandbox_active) {
+      actual_child = real_pids_to_sandbox_pids[child];
+      if (!actual_child)
+        return;
+      real_pids_to_sandbox_pids.erase(child);
+    } else {
+      actual_child = child;
+    }
+
+    ProcessWatcher::EnsureProcessTerminated(actual_child);
+  }
+
+  void HandleGetTerminationStatus(int fd, const Pickle& pickle, void* iter) {
+    base::ProcessHandle child;
+
+    if (!pickle.ReadInt(&iter, &child)) {
+      LOG(WARNING) << "Error parsing GetTerminationStatus request "
+                   << "from browser";
+      return;
+    }
+
+    base::TerminationStatus status;
+    int exit_code;
+    if (g_suid_sandbox_active)
+      child = real_pids_to_sandbox_pids[child];
+    if (child) {
+      status = base::GetTerminationStatus(child, &exit_code);
+    } else {
+      // Assume that if we can't find the child in the sandbox, then
+      // it terminated normally.
+      status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
+      exit_code = ResultCodes::NORMAL_EXIT;
+    }
+
+    Pickle write_pickle;
+    write_pickle.WriteInt(static_cast<int>(status));
+    write_pickle.WriteInt(exit_code);
+    ssize_t written =
+        HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size()));
+    if (written != static_cast<ssize_t>(write_pickle.size()))
+      PLOG(ERROR) << "write";
+  }
+
+  // This is equivalent to fork(), except that, when using the SUID
+  // sandbox, it returns the real PID of the child process as it
+  // appears outside the sandbox, rather than returning the PID inside
+  // the sandbox.
+  int ForkWithRealPid() {
+    if (!g_suid_sandbox_active)
+      return fork();
+
+    int dummy_fd;
+    ino_t dummy_inode;
+    int pipe_fds[2] = { -1, -1 };
+    base::ProcessId pid = 0;
+
+    dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+    if (dummy_fd < 0) {
+      LOG(ERROR) << "Failed to create dummy FD";
+      goto error;
+    }
+    if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) {
+      LOG(ERROR) << "Failed to get inode for dummy FD";
+      goto error;
+    }
+    if (pipe(pipe_fds) != 0) {
+      LOG(ERROR) << "Failed to create pipe";
+      goto error;
+    }
+
+    pid = fork();
+    if (pid < 0) {
+      goto error;
+    } else if (pid == 0) {
+      // In the child process.
+      close(pipe_fds[1]);
+      char buffer[1];
+      // Wait until the parent process has discovered our PID.  We
+      // should not fork any child processes (which the seccomp
+      // sandbox does) until then, because that can interfere with the
+      // parent's discovery of our PID.
+      if (HANDLE_EINTR(read(pipe_fds[0], buffer, 1)) != 1 ||
+          buffer[0] != 'x') {
+        LOG(FATAL) << "Failed to synchronise with parent zygote process";
+      }
+      close(pipe_fds[0]);
+      close(dummy_fd);
+      return 0;
+    } else {
+      // In the parent process.
+      close(dummy_fd);
+      dummy_fd = -1;
+      close(pipe_fds[0]);
+      pipe_fds[0] = -1;
+      uint8_t reply_buf[512];
+      Pickle request;
+      request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE);
+      request.WriteUInt64(dummy_inode);
+
+      const ssize_t r = UnixDomainSocket::SendRecvMsg(
+          kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL,
+          request);
+      if (r == -1) {
+        LOG(ERROR) << "Failed to get child process's real PID";
+        goto error;
+      }
+
+      base::ProcessId real_pid;
+      Pickle reply(reinterpret_cast<char*>(reply_buf), r);
+      void* iter2 = NULL;
+      if (!reply.ReadInt(&iter2, &real_pid))
+        goto error;
+      if (real_pid <= 0) {
+        // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already?
+        LOG(ERROR) << "METHOD_GET_CHILD_WITH_INODE failed";
+        goto error;
+      }
+      real_pids_to_sandbox_pids[real_pid] = pid;
+      if (HANDLE_EINTR(write(pipe_fds[1], "x", 1)) != 1) {
+        LOG(ERROR) << "Failed to synchronise with child process";
+        goto error;
+      }
+      close(pipe_fds[1]);
+      return real_pid;
+    }
+
+   error:
+    if (pid > 0) {
+      if (waitpid(pid, NULL, WNOHANG) == -1)
+        LOG(ERROR) << "Failed to wait for process";
+    }
+    if (dummy_fd >= 0)
+      close(dummy_fd);
+    if (pipe_fds[0] >= 0)
+      close(pipe_fds[0]);
+    if (pipe_fds[1] >= 0)
+      close(pipe_fds[1]);
+    return -1;
+  }
+
+  // Handle a 'fork' request from the browser: this means that the browser
+  // wishes to start a new renderer.
+  bool HandleForkRequest(int fd, const Pickle& pickle, void* iter,
+                         std::vector<int>& fds) {
+    std::vector<std::string> args;
+    int argc, numfds;
+    base::GlobalDescriptors::Mapping mapping;
+    base::ProcessId child;
+
+    if (!pickle.ReadInt(&iter, &argc))
+      goto error;
+
+    for (int i = 0; i < argc; ++i) {
+      std::string arg;
+      if (!pickle.ReadString(&iter, &arg))
+        goto error;
+      args.push_back(arg);
+    }
+
+    if (!pickle.ReadInt(&iter, &numfds))
+      goto error;
+    if (numfds != static_cast<int>(fds.size()))
+      goto error;
+
+    for (int i = 0; i < numfds; ++i) {
+      base::GlobalDescriptors::Key key;
+      if (!pickle.ReadUInt32(&iter, &key))
+        goto error;
+      mapping.push_back(std::make_pair(key, fds[i]));
+    }
+
+    mapping.push_back(std::make_pair(
+        static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor));
+
+    child = ForkWithRealPid();
+
+    if (!child) {
+#if defined(SECCOMP_SANDBOX)
+      // Try to open /proc/self/maps as the seccomp sandbox needs access to it
+      if (g_proc_fd >= 0) {
+        int proc_self_maps = openat(g_proc_fd, "self/maps", O_RDONLY);
+        if (proc_self_maps >= 0) {
+          SeccompSandboxSetProcSelfMaps(proc_self_maps);
+        }
+        close(g_proc_fd);
+        g_proc_fd = -1;
+      }
+#endif
+
+      close(kBrowserDescriptor);  // our socket from the browser
+      if (g_suid_sandbox_active)
+        close(kZygoteIdDescriptor);  // another socket from the browser
+      base::GlobalDescriptors::GetInstance()->Reset(mapping);
+
+#if defined(CHROMIUM_SELINUX)
+      SELinuxTransitionToTypeOrDie("chromium_renderer_t");
+#endif
+
+      // Reset the process-wide command line to our new command line.
+      CommandLine::Reset();
+      CommandLine::Init(0, NULL);
+      CommandLine::ForCurrentProcess()->InitFromArgv(args);
+
+      // Update the process title. The argv was already cached by the call to
+      // SetProcessTitleFromCommandLine in ChromeMain, so we can pass NULL here
+      // (we don't have the original argv at this point).
+      SetProcessTitleFromCommandLine(NULL);
+
+      // The fork() request is handled further up the call stack.
+      return true;
+    } else if (child < 0) {
+      LOG(ERROR) << "Zygote could not fork: " << errno;
+      goto error;
+    }
+
+    for (std::vector<int>::const_iterator
+         i = fds.begin(); i != fds.end(); ++i)
+      close(*i);
+
+    if (HANDLE_EINTR(write(fd, &child, sizeof(child))) < 0)
+      PLOG(ERROR) << "write";
+    return false;
+
+   error:
+    LOG(ERROR) << "Error parsing fork request from browser";
+    for (std::vector<int>::const_iterator
+         i = fds.begin(); i != fds.end(); ++i)
+      close(*i);
+    return false;
+  }
+
+  bool HandleGetSandboxStatus(int fd, const Pickle& pickle, void* iter) {
+    if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_)) !=
+                     sizeof(sandbox_flags_))) {
+      PLOG(ERROR) << "write";
+    }
+
+    return false;
+  }
+
+  // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs
+  // fork() returns are not the real PIDs, so we need to map the Real PIDS
+  // into the sandbox PID namespace.
+  typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap;
+  ProcessMap real_pids_to_sandbox_pids;
+
+  const int sandbox_flags_;
+};
+
+// With SELinux we can carve out a precise sandbox, so we don't have to play
+// with intercepting libc calls.
+#if !defined(CHROMIUM_SELINUX)
+
+static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output,
+                                        char* timezone_out,
+                                        size_t timezone_out_len) {
+  Pickle request;
+  request.WriteInt(LinuxSandbox::METHOD_LOCALTIME);
+  request.WriteString(
+      std::string(reinterpret_cast<char*>(&input), sizeof(input)));
+
+  uint8_t reply_buf[512];
+  const ssize_t r = UnixDomainSocket::SendRecvMsg(
+      kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, request);
+  if (r == -1) {
+    memset(output, 0, sizeof(struct tm));
+    return;
+  }
+
+  Pickle reply(reinterpret_cast<char*>(reply_buf), r);
+  void* iter = NULL;
+  std::string result, timezone;
+  if (!reply.ReadString(&iter, &result) ||
+      !reply.ReadString(&iter, &timezone) ||
+      result.size() != sizeof(struct tm)) {
+    memset(output, 0, sizeof(struct tm));
+    return;
+  }
+
+  memcpy(output, result.data(), sizeof(struct tm));
+  if (timezone_out_len) {
+    const size_t copy_len = std::min(timezone_out_len - 1, timezone.size());
+    memcpy(timezone_out, timezone.data(), copy_len);
+    timezone_out[copy_len] = 0;
+    output->tm_zone = timezone_out;
+  } else {
+    output->tm_zone = NULL;
+  }
+}
+
+static bool g_am_zygote_or_renderer = false;
+
+// Sandbox interception of libc calls.
+//
+// Because we are running in a sandbox certain libc calls will fail (localtime
+// being the motivating example - it needs to read /etc/localtime). We need to
+// intercept these calls and proxy them to the browser. However, these calls
+// may come from us or from our libraries. In some cases we can't just change
+// our code.
+//
+// It's for these cases that we have the following setup:
+//
+// We define global functions for those functions which we wish to override.
+// Since we will be first in the dynamic resolution order, the dynamic linker
+// will point callers to our versions of these functions. However, we have the
+// same binary for both the browser and the renderers, which means that our
+// overrides will apply in the browser too.
+//
+// The global |g_am_zygote_or_renderer| is true iff we are in a zygote or
+// renderer process. It's set in ZygoteMain and inherited by the renderers when
+// they fork. (This means that it'll be incorrect for global constructor
+// functions and before ZygoteMain is called - beware).
+//
+// Our replacement functions can check this global and either proxy
+// the call to the browser over the sandbox IPC
+// (http://code.google.com/p/chromium/wiki/LinuxSandboxIPC) or they can use
+// dlsym with RTLD_NEXT to resolve the symbol, ignoring any symbols in the
+// current module.
+//
+// Other avenues:
+//
+// Our first attempt involved some assembly to patch the GOT of the current
+// module. This worked, but was platform specific and doesn't catch the case
+// where a library makes a call rather than current module.
+//
+// We also considered patching the function in place, but this would again by
+// platform specific and the above technique seems to work well enough.
+
+typedef struct tm* (*LocaltimeFunction)(const time_t* timep);
+typedef struct tm* (*LocaltimeRFunction)(const time_t* timep,
+                                         struct tm* result);
+
+static pthread_once_t g_libc_localtime_funcs_guard = PTHREAD_ONCE_INIT;
+static LocaltimeFunction g_libc_localtime;
+static LocaltimeRFunction g_libc_localtime_r;
+
+static void InitLibcLocaltimeFunctions() {
+  g_libc_localtime = reinterpret_cast<LocaltimeFunction>(
+                         dlsym(RTLD_NEXT, "localtime"));
+  g_libc_localtime_r = reinterpret_cast<LocaltimeRFunction>(
+                         dlsym(RTLD_NEXT, "localtime_r"));
+
+  if (!g_libc_localtime || !g_libc_localtime_r) {
+    // http://code.google.com/p/chromium/issues/detail?id=16800
+    //
+    // Nvidia's libGL.so overrides dlsym for an unknown reason and replaces
+    // it with a version which doesn't work. In this case we'll get a NULL
+    // result. There's not a lot we can do at this point, so we just bodge it!
+    LOG(ERROR) << "Your system is broken: dlsym doesn't work! This has been "
+                  "reported to be caused by Nvidia's libGL. You should expect"
+                  " time related functions to misbehave. "
+                  "http://code.google.com/p/chromium/issues/detail?id=16800";
+  }
+
+  if (!g_libc_localtime)
+    g_libc_localtime = gmtime;
+  if (!g_libc_localtime_r)
+    g_libc_localtime_r = gmtime_r;
+}
+
+struct tm* localtime(const time_t* timep) {
+  if (g_am_zygote_or_renderer) {
+    static struct tm time_struct;
+    static char timezone_string[64];
+    ProxyLocaltimeCallToBrowser(*timep, &time_struct, timezone_string,
+                                sizeof(timezone_string));
+    return &time_struct;
+  } else {
+    CHECK_EQ(0, pthread_once(&g_libc_localtime_funcs_guard,
+                             InitLibcLocaltimeFunctions));
+    return g_libc_localtime(timep);
+  }
+}
+
+struct tm* localtime_r(const time_t* timep, struct tm* result) {
+  if (g_am_zygote_or_renderer) {
+    ProxyLocaltimeCallToBrowser(*timep, result, NULL, 0);
+    return result;
+  } else {
+    CHECK_EQ(0, pthread_once(&g_libc_localtime_funcs_guard,
+                             InitLibcLocaltimeFunctions));
+    return g_libc_localtime_r(timep, result);
+  }
+}
+
+#endif  // !CHROMIUM_SELINUX
+
+// This function triggers the static and lazy construction of objects that need
+// to be created before imposing the sandbox.
+static void PreSandboxInit() {
+  base::RandUint64();
+
+  base::SysInfo::MaxSharedMemorySize();
+
+  // ICU DateFormat class (used in base/time_format.cc) needs to get the
+  // Olson timezone ID by accessing the zoneinfo files on disk. After
+  // TimeZone::createDefault is called once here, the timezone ID is
+  // cached and there's no more need to access the file system.
+  scoped_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
+
+  FilePath module_path;
+  if (PathService::Get(base::DIR_MODULE, &module_path))
+    media::InitializeMediaLibrary(module_path);
+
+  // Ensure access to the Pepper plugins before the sandbox is turned on.
+  PepperPluginRegistry::PreloadModules();
+}
+
+#if !defined(CHROMIUM_SELINUX)
+static bool EnterSandbox() {
+  // The SUID sandbox sets this environment variable to a file descriptor
+  // over which we can signal that we have completed our startup and can be
+  // chrooted.
+  const char* const sandbox_fd_string = getenv("SBX_D");
+
+  if (sandbox_fd_string) {
+    // Use the SUID sandbox.  This still allows the seccomp sandbox to
+    // be enabled by the process later.
+    g_suid_sandbox_active = true;
+
+    char* endptr;
+    const long fd_long = strtol(sandbox_fd_string, &endptr, 10);
+    if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX)
+      return false;
+    const int fd = fd_long;
+
+    PreSandboxInit();
+
+    static const char kMsgChrootMe = 'C';
+    static const char kMsgChrootSuccessful = 'O';
+
+    if (HANDLE_EINTR(write(fd, &kMsgChrootMe, 1)) != 1) {
+      LOG(ERROR) << "Failed to write to chroot pipe: " << errno;
+      return false;
+    }
+
+    // We need to reap the chroot helper process in any event:
+    wait(NULL);
+
+    char reply;
+    if (HANDLE_EINTR(read(fd, &reply, 1)) != 1) {
+      LOG(ERROR) << "Failed to read from chroot pipe: " << errno;
+      return false;
+    }
+
+    if (reply != kMsgChrootSuccessful) {
+      LOG(ERROR) << "Error code reply from chroot helper";
+      return false;
+    }
+
+    SkiaFontConfigSetImplementation(
+        new FontConfigIPC(kMagicSandboxIPCDescriptor));
+
+    // Previously, we required that the binary be non-readable. This causes the
+    // kernel to mark the process as non-dumpable at startup. The thinking was
+    // that, although we were putting the renderers into a PID namespace (with
+    // the SUID sandbox), they would nonetheless be in the /same/ PID
+    // namespace. So they could ptrace each other unless they were non-dumpable.
+    //
+    // If the binary was readable, then there would be a window between process
+    // startup and the point where we set the non-dumpable flag in which a
+    // compromised renderer could ptrace attach.
+    //
+    // However, now that we have a zygote model, only the (trusted) zygote
+    // exists at this point and we can set the non-dumpable flag which is
+    // inherited by all our renderer children.
+    //
+    // Note: a non-dumpable process can't be debugged. To debug sandbox-related
+    // issues, one can specify --allow-sandbox-debugging to let the process be
+    // dumpable.
+    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+    if (!command_line.HasSwitch(switches::kAllowSandboxDebugging)) {
+      prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
+      if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+        LOG(ERROR) << "Failed to set non-dumpable flag";
+        return false;
+      }
+    }
+  } else if (switches::SeccompSandboxEnabled()) {
+    PreSandboxInit();
+    SkiaFontConfigSetImplementation(
+        new FontConfigIPC(kMagicSandboxIPCDescriptor));
+  } else {
+    SkiaFontConfigUseDirectImplementation();
+  }
+
+  return true;
+}
+#else  // CHROMIUM_SELINUX
+
+static bool EnterSandbox() {
+  PreSandboxInit();
+  SkiaFontConfigUseIPCImplementation(kMagicSandboxIPCDescriptor);
+  return true;
+}
+
+#endif  // CHROMIUM_SELINUX
+
+bool ZygoteMain(const MainFunctionParams& params) {
+#if !defined(CHROMIUM_SELINUX)
+  g_am_zygote_or_renderer = true;
+#endif
+
+#if defined(SECCOMP_SANDBOX)
+  // The seccomp sandbox needs access to files in /proc, which might be denied
+  // after one of the other sandboxes have been started. So, obtain a suitable
+  // file handle in advance.
+  if (switches::SeccompSandboxEnabled()) {
+    g_proc_fd = open("/proc", O_DIRECTORY | O_RDONLY);
+    if (g_proc_fd < 0) {
+      LOG(ERROR) << "WARNING! Cannot access \"/proc\". Disabling seccomp "
+                    "sandboxing.";
+    }
+  }
+#endif  // SECCOMP_SANDBOX
+
+  // Turn on the SELinux or SUID sandbox
+  if (!EnterSandbox()) {
+    LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: "
+               << errno << ")";
+    return false;
+  }
+
+  int sandbox_flags = 0;
+  if (getenv("SBX_D"))
+    sandbox_flags |= ZygoteHost::kSandboxSUID;
+  if (getenv("SBX_PID_NS"))
+    sandbox_flags |= ZygoteHost::kSandboxPIDNS;
+  if (getenv("SBX_NET_NS"))
+    sandbox_flags |= ZygoteHost::kSandboxNetNS;
+
+#if defined(SECCOMP_SANDBOX)
+  // The seccomp sandbox will be turned on when the renderers start. But we can
+  // already check if sufficient support is available so that we only need to
+  // print one error message for the entire browser session.
+  if (g_proc_fd >= 0 && switches::SeccompSandboxEnabled()) {
+    if (!SupportsSeccompSandbox(g_proc_fd)) {
+      // There are a good number of users who cannot use the seccomp sandbox
+      // (e.g. because their distribution does not enable seccomp mode by
+      // default). While we would prefer to deny execution in this case, it
+      // seems more realistic to continue in degraded mode.
+      LOG(ERROR) << "WARNING! This machine lacks support needed for the "
+                    "Seccomp sandbox. Running renderers with Seccomp "
+                    "sandboxing disabled.";
+    } else {
+      VLOG(1) << "Enabling experimental Seccomp sandbox.";
+      sandbox_flags |= ZygoteHost::kSandboxSeccomp;
+    }
+  }
+#endif  // SECCOMP_SANDBOX
+
+  Zygote zygote(sandbox_flags);
+  // This function call can return multiple times, once per fork().
+  return zygote.ProcessRequests();
+}
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 4a5a09a..f497bf5 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -46,6 +46,23 @@
         'browser/cross_site_request_manager.h',
         'browser/disposition_utils.cc',
         'browser/disposition_utils.h',
+        'browser/gpu_blacklist.cc',
+        'browser/gpu_blacklist.h',
+        'browser/gpu_process_host.cc',
+        'browser/gpu_process_host.h',
+        'browser/host_zoom_map.cc',
+        'browser/host_zoom_map.h',
+        'browser/mime_registry_message_filter.cc',
+        'browser/mime_registry_message_filter.h',
+        'browser/modal_html_dialog_delegate.cc',
+        'browser/modal_html_dialog_delegate.h',
+        'browser/ppapi_plugin_process_host.cc',
+        'browser/ppapi_plugin_process_host.h',
+        'browser/plugin_process_host.cc',
+        'browser/plugin_process_host.h',
+        'browser/plugin_process_host_mac.cc',
+        'browser/plugin_service.cc',
+        'browser/plugin_service.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',
@@ -156,6 +173,9 @@
         'browser/tab_contents/tab_contents_observer.h',
         'browser/tab_contents/tab_contents_view.cc',
         'browser/tab_contents/tab_contents_view.h',
+        'browser/zygote_host_linux.cc',
+        'browser/zygote_host_linux.h',
+        'browser/zygote_main_linux.cc',
       ],
       'conditions': [
         ['OS=="win"', {
@@ -177,6 +197,14 @@
             'browser/certificate_manager_model.h',
           ],
         }],
+        ['OS=="mac"', {
+          'link_settings': {
+            'mac_bundle_resources': [
+              'browser/gpu.sb',
+              'browser/worker.sb',
+            ],
+          },
+        }],
       ],
     },
   ],