summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-23 03:55:40 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-23 03:55:40 +0000
commita0421736a8a437ff97eb7deb6050f08b75810343 (patch)
tree0df2ac04070f1fa41b1a3dfbd5582f0f9bb9817e /content
parenta6d8357a9702c6ce48e15914760708c1970a03e2 (diff)
downloadchromium_src-a0421736a8a437ff97eb7deb6050f08b75810343.zip
chromium_src-a0421736a8a437ff97eb7deb6050f08b75810343.tar.gz
chromium_src-a0421736a8a437ff97eb7deb6050f08b75810343.tar.bz2
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
Diffstat (limited to 'content')
-rw-r--r--content/DEPS1
-rw-r--r--content/browser/gpu.sb18
-rw-r--r--content/browser/gpu_blacklist.cc577
-rw-r--r--content/browser/gpu_blacklist.h227
-rw-r--r--content/browser/gpu_blacklist_unittest.cc176
-rw-r--r--content/browser/gpu_process_host.cc287
-rw-r--r--content/browser/gpu_process_host.h54
-rw-r--r--content/browser/host_zoom_map.cc260
-rw-r--r--content/browser/host_zoom_map.h127
-rw-r--r--content/browser/host_zoom_map_unittest.cc132
-rw-r--r--content/browser/mime_registry_message_filter.cc51
-rw-r--r--content/browser/mime_registry_message_filter.h31
-rw-r--r--content/browser/modal_html_dialog_delegate.cc77
-rw-r--r--content/browser/modal_html_dialog_delegate.h68
-rw-r--r--content/browser/plugin_process_host.cc478
-rw-r--r--content/browser/plugin_process_host.h177
-rw-r--r--content/browser/plugin_process_host_mac.cc111
-rw-r--r--content/browser/plugin_service.cc584
-rw-r--r--content/browser/plugin_service.h232
-rw-r--r--content/browser/plugin_service_browsertest.cc107
-rw-r--r--content/browser/plugin_service_unittest.cc62
-rw-r--r--content/browser/ppapi_plugin_process_host.cc174
-rw-r--r--content/browser/ppapi_plugin_process_host.h78
-rw-r--r--content/browser/worker.sb12
-rw-r--r--content/browser/zygote_host_linux.cc362
-rw-r--r--content/browser/zygote_host_linux.h98
-rw-r--r--content/browser/zygote_main_linux.cc753
-rw-r--r--content/content_browser.gypi28
28 files changed, 5342 insertions, 0 deletions
diff --git a/content/DEPS b/content/DEPS
index bd39303..ffab3e2 100644
--- a/content/DEPS
+++ b/content/DEPS
@@ -13,6 +13,7 @@ include_rules = [
"+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..4ef366a
--- /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..87b5b24
--- /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..ad44aae
--- /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..5ead5f5
--- /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',
+ ],
+ },
+ }],
],
},
],