Index: chrome/common/extensions/api/sockets/sockets_handler.cc |
diff --git a/chrome/common/extensions/api/sockets/sockets_handler.cc b/chrome/common/extensions/api/sockets/sockets_handler.cc |
index a1b51c21e3a0974a19e75b779742eb714ed162a5..3cdcd9786bab345c7968f2e63c0b173620f270f3 100644 |
--- a/chrome/common/extensions/api/sockets/sockets_handler.cc |
+++ b/chrome/common/extensions/api/sockets/sockets_handler.cc |
@@ -9,11 +9,16 @@ |
#include "base/values.h" |
#include "chrome/common/extensions/api/manifest_types.h" |
#include "chrome/common/extensions/extension.h" |
+#include "chrome/common/extensions/extension_messages.h" |
#include "chrome/common/extensions/permissions/permissions_data.h" |
#include "chrome/common/extensions/permissions/socket_permission_data.h" |
#include "extensions/common/error_utils.h" |
#include "extensions/common/manifest_constants.h" |
#include "extensions/common/permissions/api_permission_set.h" |
+#include "grit/generated_resources.h" |
+#include "ipc/ipc_message.h" |
+#include "ipc/ipc_message_utils.h" |
+#include "ui/base/l10n/l10n_util.h" |
namespace extensions { |
@@ -24,6 +29,221 @@ const char kErrorInvalidHostPattern[] = "Invalid host:port pattern '*'"; |
namespace keys = extensions::manifest_keys; |
namespace errors = sockets_errors; |
using api::manifest_types::Sockets; |
+using content::SocketPermissionRequest; |
+ |
+// TODO(rpaquay): Unit test for all this class (Diff, Union, etc). |
+class SocketsManifestPermission : public ManifestPermission { |
Yoyo Zhou
2013/11/09 01:15:30
Do you anticipate something like SetDisjunctionPer
rpaquay
2013/11/11 18:37:35
I would say probably at some point. I don't know o
Yoyo Zhou
2013/11/12 02:39:29
Okay.
|
+ public: |
+ SocketsManifestPermission() { |
+ } |
+ |
+ explicit SocketsManifestPermission(scoped_ptr<SocketsManifestData> data) |
+ : data_(data.Pass()) { |
+ } |
+ |
+ virtual std::string name() const OVERRIDE { |
+ return keys::kSockets; |
+ } |
+ |
+ virtual std::string id() const OVERRIDE { |
+ return name(); |
+ } |
+ |
+ // Returns true if this permission has any PermissionMessages. |
+ virtual bool HasMessages() const OVERRIDE { |
+ if (!data_) |
+ return false; |
+ |
+ return data_->HasMessages(); |
+ } |
+ |
+ // Returns the localized permission messages of this permission. |
+ virtual PermissionMessages GetMessages() const OVERRIDE { |
+ if (!data_) |
+ return PermissionMessages(); |
+ return data_->GetPermissionMessages(); |
+ } |
+ |
+ // Parses the ManifestPermission from |value|. Returns false if error happens. |
+ virtual bool FromValue(const base::Value* value) OVERRIDE { |
+ // TODO(rpaquay): Null check? |
+ if (!value) |
+ return false; |
+ |
+ std::vector<InstallWarning> warnings; |
+ string16 error; |
+ scoped_ptr<SocketsManifestData> data( |
+ SocketsManifestData::FromValue(*value, &warnings, &error)); |
+ |
+ if (!data) |
+ return false; |
+ |
+ data_ = data.Pass(); |
+ return true; |
+ } |
+ |
+ // Stores this into a new created |value|. |
+ virtual scoped_ptr<base::Value> ToValue() const OVERRIDE { |
+ if (!data_) { |
+ // TODO(rpaquay) : Is it ok to return NULL? |
+ return scoped_ptr<base::Value>(base::Value::CreateNullValue()); |
+ } |
+ |
+ Sockets sockets; |
+ if (data_->is_udp()) { |
+ sockets.udp.reset(new Sockets::Udp()); |
+ sockets.udp->bind = AddEntry(SocketPermissionRequest::UDP_BIND).Pass(); |
+ sockets.udp->send = AddEntry(SocketPermissionRequest::UDP_SEND_TO).Pass(); |
+ sockets.udp->multicast_membership = |
+ AddEntry(SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP).Pass(); |
+ } |
+ if (data_->is_tcp()) { |
+ sockets.tcp.reset(new Sockets::Tcp()); |
+ sockets.tcp->connect = AddEntry( |
+ SocketPermissionRequest::TCP_CONNECT).Pass(); |
+ } |
+ if (data_->is_tcp_server()) { |
+ sockets.tcp_server.reset(new Sockets::TcpServer()); |
+ sockets.tcp_server->listen = AddEntry( |
+ SocketPermissionRequest::TCP_LISTEN).Pass(); |
+ } |
+ |
+ return sockets.ToValue().Pass(); |
+ } |
+ |
+ // Clones this. |
+ virtual ManifestPermission* Clone() const OVERRIDE { |
+ if (!data_) |
+ return new SocketsManifestPermission(); |
+ |
+ scoped_ptr<SocketsManifestData> clone_data(data_->Clone()); |
+ return new SocketsManifestPermission(clone_data.Pass()); |
+ } |
+ |
+ // Returns a new API permission which equals this - |rhs|. |
+ virtual ManifestPermission* Diff(const ManifestPermission* rhs) |
+ const OVERRIDE { |
+ const SocketsManifestPermission* other = |
+ static_cast<const SocketsManifestPermission*>(rhs); |
+ |
+ if (!data_) |
+ return new SocketsManifestPermission(); |
+ |
+ if (!other->data_) |
+ return Clone(); |
+ |
+ scoped_ptr<SocketsManifestData> data(data_->Diff(other->data_.get())); |
+ return new SocketsManifestPermission(data.Pass()); |
+ } |
+ |
+ // Returns a new API permission which equals the union of this and |rhs|. |
+ virtual ManifestPermission* Union(const ManifestPermission* rhs) |
+ const OVERRIDE { |
+ const SocketsManifestPermission* other = |
+ static_cast<const SocketsManifestPermission*>(rhs); |
+ |
+ if (!data_) |
+ return other->Clone(); |
+ |
+ if (!other->data_) |
+ return Clone(); |
+ |
+ scoped_ptr<SocketsManifestData> data(data_->Union(other->data_.get())); |
+ return new SocketsManifestPermission(data.Pass()); |
+ } |
+ |
+ // Returns a new API permission which equals the intersect of this and |rhs|. |
+ virtual ManifestPermission* Intersect(const ManifestPermission* rhs) |
+ const OVERRIDE { |
+ const SocketsManifestPermission* other = |
+ static_cast<const SocketsManifestPermission*>(rhs); |
+ |
+ if (!data_) |
+ return new SocketsManifestPermission(); |
+ |
+ if (!other->data_) |
+ return new SocketsManifestPermission(); |
+ |
+ scoped_ptr<SocketsManifestData> data(data_->Intersect(other->data_.get())); |
+ return new SocketsManifestPermission(data.Pass()); |
+ } |
+ |
+ // Returns true if |rhs| is a subset of this. |
+ virtual bool Contains(const ManifestPermission* rhs) const OVERRIDE { |
+ const SocketsManifestPermission* other = |
+ static_cast<const SocketsManifestPermission*>(rhs); |
+ |
+ if (!data_) |
+ return !other->data_; |
+ |
+ if (!other->data_) |
+ return true; |
+ |
+ return data_->Contains(other->data_.get()); |
+ } |
+ |
+ // Returns true if |rhs| is equal to this. |
+ virtual bool Equal(const ManifestPermission* rhs) const OVERRIDE { |
+ const SocketsManifestPermission* other = |
+ static_cast<const SocketsManifestPermission*>(rhs); |
+ |
+ if (!data_) |
+ return !other->data_; |
+ |
+ if (!other->data_) |
+ return false; |
+ |
+ return data_->Equal(other->data_.get()); |
+ } |
+ |
+ // IPC functions |
+ // Writes this into the given IPC message |m|. |
+ virtual void Write(IPC::Message* m) const OVERRIDE { |
+ bool has_data = !!data_; |
+ IPC::WriteParam(m, has_data); |
+ if (has_data) { |
+ data_->Write(m); |
+ } |
+ } |
+ |
+ // Reads from the given IPC message |m|. |
+ virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE { |
+ data_.reset(); |
+ |
+ bool has_data; |
+ bool result = IPC::ReadParam(m, iter, &has_data); |
+ if (!result) |
+ return result; |
+ |
+ if (!has_data) |
+ return true; |
+ |
+ data_.reset(new SocketsManifestData()); |
+ return data_->Read(m, iter); |
+ } |
+ |
+ // Logs this permission. |
+ virtual void Log(std::string* log) const OVERRIDE { |
+ if (data_) |
+ data_->Log(log); |
+ } |
+ |
+ private: |
+ scoped_ptr<std::string> AddEntry( |
+ content::SocketPermissionRequest::OperationType operation_type) const { |
+ scoped_ptr<std::string> result; |
+ for (SocketsManifestData::SocketPermissionEntrySet::const_iterator it = |
+ data_->entries().begin(); it != data_->entries().end() ; ++it) { |
+ if (it->pattern().type == operation_type) { |
+ result.reset(new std::string(it->GetHostPatternAsString())); |
+ break; |
+ } |
+ } |
+ return result.Pass(); |
+ } |
+ |
+ scoped_ptr<SocketsManifestData> data_; |
Yoyo Zhou
2013/11/09 01:15:30
This looks like unusual ownership. ManifestDatas a
rpaquay
2013/11/11 18:37:35
The "initial" value is a clone of the extension ma
Yoyo Zhou
2013/11/13 02:57:07
(SocketPermissionData already exists, but it seems
rpaquay
2013/11/13 21:28:55
This makes sense. Patchset #6 swaps ownership betw
|
+}; |
SocketsHandler::SocketsHandler() {} |
@@ -45,11 +265,25 @@ bool SocketsHandler::Parse(Extension* extension, string16* error) { |
return true; |
} |
+ManifestPermission* SocketsHandler::CreatePermission() { |
+ return new SocketsManifestPermission(); |
+} |
+ |
+ManifestPermission* SocketsHandler::CreateInitialRequiredPermission( |
+ const Extension* extension) { |
+ SocketsManifestData* data = SocketsManifestData::Get(extension); |
+ if (data) { |
+ return new SocketsManifestPermission( |
+ scoped_ptr<SocketsManifestData>(data->Clone()).Pass()); |
+ } |
+ return new SocketsManifestPermission(); |
+} |
+ |
const std::vector<std::string> SocketsHandler::Keys() const { |
return SingleKey(manifest_keys::kSockets); |
} |
-SocketsManifestData::SocketsManifestData() {} |
+SocketsManifestData::SocketsManifestData() : kinds_(kNone) {} |
SocketsManifestData::~SocketsManifestData() {} |
// static |
@@ -63,7 +297,7 @@ bool SocketsManifestData::CheckRequest( |
const Extension* extension, |
const content::SocketPermissionRequest& request) { |
SocketsManifestData* data = SocketsManifestData::Get(extension); |
- if (data == NULL) |
+ if (!data) |
return false; |
return data->CheckRequestImpl(extension, request); |
@@ -80,6 +314,7 @@ scoped_ptr<SocketsManifestData> SocketsManifestData::FromValue( |
scoped_ptr<SocketsManifestData> result(new SocketsManifestData()); |
if (sockets->udp) { |
+ result->kinds_ |= kUdpPermission; |
if (!ParseHostPattern(result.get(), |
content::SocketPermissionRequest::UDP_BIND, |
sockets->udp->bind, |
@@ -100,6 +335,7 @@ scoped_ptr<SocketsManifestData> SocketsManifestData::FromValue( |
} |
} |
if (sockets->tcp) { |
+ result->kinds_ |= kTcpPermission; |
if (!ParseHostPattern(result.get(), |
content::SocketPermissionRequest::TCP_CONNECT, |
sockets->tcp->connect, |
@@ -108,6 +344,7 @@ scoped_ptr<SocketsManifestData> SocketsManifestData::FromValue( |
} |
} |
if (sockets->tcp_server) { |
+ result->kinds_ |= kTcpServerPermission; |
if (!ParseHostPattern(result.get(), |
content::SocketPermissionRequest::TCP_LISTEN, |
sockets->tcp_server->listen, |
@@ -144,7 +381,7 @@ void SocketsManifestData::AddPermission(const SocketPermissionEntry& entry) { |
bool SocketsManifestData::CheckRequestImpl( |
const Extension* extension, |
const content::SocketPermissionRequest& request) { |
- for (PermissionSet::const_iterator it = permissions_.begin(); |
+ for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); |
it != permissions_.end(); ++it) { |
if (it->Check(request)) |
return true; |
@@ -152,4 +389,167 @@ bool SocketsManifestData::CheckRequestImpl( |
return false; |
} |
+bool SocketsManifestData::HasMessages() const { |
+ bool is_empty = permissions_.empty() && (kinds_ == kNone); |
+ return !is_empty; |
+} |
+ |
+PermissionMessages SocketsManifestData::GetPermissionMessages() const { |
+ // TODO(rpaquay): This function and callees is (almost) a copy/paste |
+ // from |extensions::SocketPermission|. |
+ PermissionMessages result; |
+ if (!AddAnyHostMessage(result)) { |
+ AddSpecificHostMessage(result); |
+ AddSubdomainHostMessage(result); |
+ } |
+ AddNetworkListMessage(result); |
+ return result; |
+} |
+ |
+SocketsManifestData* SocketsManifestData::Diff(const SocketsManifestData* rhs) |
+ const { |
+ scoped_ptr<SocketsManifestData> data(new SocketsManifestData()); |
+ std::set_difference( |
+ permissions_.begin(), permissions_.end(), |
+ rhs->permissions_.begin(), rhs->permissions_.end(), |
+ std::inserter<SocketPermissionEntrySet>( |
+ data->permissions_, data->permissions_.begin())); |
+ |
+ data->kinds_ = (kinds_ & (~rhs->kinds_)); |
+ return data.release(); |
+} |
+ |
+SocketsManifestData* SocketsManifestData::Union(const SocketsManifestData* rhs) |
+ const { |
+ scoped_ptr<SocketsManifestData> data(new SocketsManifestData()); |
+ std::set_union( |
+ permissions_.begin(), permissions_.end(), |
+ rhs->permissions_.begin(), rhs->permissions_.end(), |
+ std::inserter<SocketPermissionEntrySet>( |
+ data->permissions_, data->permissions_.begin())); |
+ |
+ data->kinds_ = (kinds_ | rhs->kinds_); |
+ return data.release(); |
+} |
+ |
+SocketsManifestData* SocketsManifestData::Intersect( |
+ const SocketsManifestData* rhs) const { |
+ scoped_ptr<SocketsManifestData> data(new SocketsManifestData()); |
+ std::set_intersection( |
+ permissions_.begin(), permissions_.end(), |
+ rhs->permissions_.begin(), rhs->permissions_.end(), |
+ std::inserter<SocketPermissionEntrySet>( |
+ data->permissions_, data->permissions_.begin())); |
+ |
+ data->kinds_ = (kinds_ & rhs->kinds_); |
+ return data.release(); |
+} |
+ |
+bool SocketsManifestData::Contains(const SocketsManifestData* rhs) const { |
+ return std::includes( |
+ entries().begin(), entries().end(), |
+ rhs->entries().begin(), rhs->entries().end()) && |
+ ((kinds_ | rhs->kinds_) == kinds_); |
+} |
+ |
+bool SocketsManifestData::Equal(const SocketsManifestData* rhs) const { |
+ return (permissions_ == rhs->permissions_) && |
+ (kinds_ == rhs->kinds_); |
+} |
+ |
+SocketsManifestData* SocketsManifestData::Clone() const { |
+ scoped_ptr<SocketsManifestData> result(new SocketsManifestData()); |
+ result->permissions_ = permissions_; |
+ result->kinds_ = kinds_; |
+ return result.release(); |
+} |
+ |
+void SocketsManifestData::Write(IPC::Message* m) const { |
+ IPC::WriteParam(m, permissions_); |
+ IPC::WriteParam(m, kinds_); |
+} |
+ |
+bool SocketsManifestData::Read(const IPC::Message* m, PickleIterator* iter) { |
+ return IPC::ReadParam(m, iter, &permissions_) && |
+ IPC::ReadParam(m, iter, &kinds_); |
+} |
+ |
+void SocketsManifestData::Log(std::string* log) const { |
+ IPC::LogParam(permissions_, log); |
+ IPC::LogParam(kinds_, log); |
+} |
+ |
+bool SocketsManifestData::AddAnyHostMessage( |
+ PermissionMessages& messages) const { |
+ for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); |
+ it != permissions_.end(); ++it) { |
+ if (it->IsAddressBoundType() && |
+ it->GetHostType() == SocketPermissionEntry::ANY_HOST) { |
+ messages.push_back(PermissionMessage( |
+ PermissionMessage::kSocketAnyHost, |
+ l10n_util::GetStringUTF16( |
+ IDS_EXTENSION_PROMPT_WARNING_SOCKET_ANY_HOST))); |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+void SocketsManifestData::AddSubdomainHostMessage( |
+ PermissionMessages& messages) const { |
+ std::set<string16> domains; |
+ for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); |
+ it != permissions_.end(); ++it) { |
+ if (it->GetHostType() == SocketPermissionEntry::HOSTS_IN_DOMAINS) |
+ domains.insert(UTF8ToUTF16(it->pattern().host)); |
+ } |
+ if (!domains.empty()) { |
+ int id = (domains.size() == 1) ? |
+ IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAIN : |
+ IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAINS; |
+ messages.push_back(PermissionMessage( |
+ PermissionMessage::kSocketDomainHosts, |
+ l10n_util::GetStringFUTF16( |
+ id, |
+ JoinString( |
+ std::vector<string16>( |
+ domains.begin(), domains.end()), ' ')))); |
+ } |
+} |
+ |
+void SocketsManifestData::AddSpecificHostMessage( |
+ PermissionMessages& messages) const { |
+ std::set<string16> hostnames; |
+ for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); |
+ it != permissions_.end(); ++it) { |
+ if (it->GetHostType() == SocketPermissionEntry::SPECIFIC_HOSTS) |
+ hostnames.insert(UTF8ToUTF16(it->pattern().host)); |
+ } |
+ if (!hostnames.empty()) { |
+ int id = (hostnames.size() == 1) ? |
+ IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOST : |
+ IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOSTS; |
+ messages.push_back(PermissionMessage( |
+ PermissionMessage::kSocketSpecificHosts, |
+ l10n_util::GetStringFUTF16( |
+ id, |
+ JoinString( |
+ std::vector<string16>( |
+ hostnames.begin(), hostnames.end()), ' ')))); |
+ } |
+} |
+ |
+void SocketsManifestData::AddNetworkListMessage( |
+ PermissionMessages& messages) const { |
+ for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); |
+ it != permissions_.end(); ++it) { |
+ if (it->pattern().type == content::SocketPermissionRequest::NETWORK_STATE) { |
+ messages.push_back(PermissionMessage( |
+ PermissionMessage::kNetworkState, |
+ l10n_util::GetStringUTF16( |
+ IDS_EXTENSION_PROMPT_WARNING_NETWORK_STATE))); |
+ } |
+ } |
+} |
+ |
} // namespace extensions |