Chromium Code Reviews| 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 |