Merge from Chromium at DEPS revision 225410

This commit was generated by merge_to_master.py.

Change-Id: Ifa1539ca216abb163295ee7a77f81bb67f52e178
diff --git a/chrome/common/extensions/permissions/api_permission.h b/chrome/common/extensions/permissions/api_permission.h
index 311aa3f..2775d78 100644
--- a/chrome/common/extensions/permissions/api_permission.h
+++ b/chrome/common/extensions/permissions/api_permission.h
@@ -151,6 +151,7 @@
     kWebRequest,
     kWebRequestBlocking,
     kWebRequestInternal,
+    kWebrtcLoggingPrivate,
     kWebstorePrivate,
     kWebView,
     kSystemCpu,
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index f57c58e..308aaef 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -234,6 +234,8 @@
     { APIPermission::kEnterprisePlatformKeysPrivate,
       "enterprise.platformKeysPrivate",
       APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kWebrtcLoggingPrivate, "webrtcLoggingPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
 
     // Full url access permissions.
     { APIPermission::kDebugger, "debugger",
diff --git a/chrome/common/extensions/permissions/permission_message.cc b/chrome/common/extensions/permissions/permission_message.cc
index b792ae9..006e91a 100644
--- a/chrome/common/extensions/permissions/permission_message.cc
+++ b/chrome/common/extensions/permissions/permission_message.cc
@@ -45,17 +45,27 @@
     default:
       message_id = kHosts4OrMore;
 
-    message = l10n_util::GetStringFUTF16(
-        IDS_EXTENSION_PROMPT_WARNING_HOSTS,
-        base::IntToString16(host_list.size()));
+      const int kRetainedFilesMessageIDs[6] = {
+          IDS_EXTENSION_PROMPT_WARNING_HOSTS_DEFAULT,
+          IDS_EXTENSION_PROMPT_WARNING_HOST_SINGULAR,
+          IDS_EXTENSION_PROMPT_WARNING_HOSTS_ZERO,
+          IDS_EXTENSION_PROMPT_WARNING_HOSTS_TWO,
+          IDS_EXTENSION_PROMPT_WARNING_HOSTS_FEW,
+          IDS_EXTENSION_PROMPT_WARNING_HOSTS_MANY,
+      };
+      std::vector<int> message_ids;
+      for (size_t i = 0; i < arraysize(kRetainedFilesMessageIDs); i++) {
+        message_ids.push_back(kRetainedFilesMessageIDs[i]);
+      }
+      message = l10n_util::GetPluralStringFUTF16(message_ids, host_list.size());
 
-    for (size_t i = 0; i < host_list.size(); ++i) {
-      if (i > 0)
-        details += ASCIIToUTF16("\n");
-      details += l10n_util::GetStringFUTF16(
-          IDS_EXTENSION_PROMPT_WARNING_HOST_LIST_ENTRY,
-          UTF8ToUTF16(host_list[i]));
-    }
+      for (size_t i = 0; i < host_list.size(); ++i) {
+        if (i > 0)
+          details += ASCIIToUTF16("\n");
+        details += l10n_util::GetStringFUTF16(
+            IDS_EXTENSION_PROMPT_WARNING_HOST_LIST_ENTRY,
+            UTF8ToUTF16(host_list[i]));
+      }
   }
 
   return PermissionMessage(message_id, message, details);
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index 9991ecc..cd4a2e1 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -729,6 +729,7 @@
   skip.insert(APIPermission::kVirtualKeyboardPrivate);
   skip.insert(APIPermission::kWallpaperPrivate);
   skip.insert(APIPermission::kWebRequestInternal);
+  skip.insert(APIPermission::kWebrtcLoggingPrivate);
   skip.insert(APIPermission::kWebstorePrivate);
 
   // Warned as part of host permissions.
diff --git a/chrome/common/extensions/permissions/permissions_data_unittest.cc b/chrome/common/extensions/permissions/permissions_data_unittest.cc
index d1b70e9..1fc56c5 100644
--- a/chrome/common/extensions/permissions/permissions_data_unittest.cc
+++ b/chrome/common/extensions/permissions/permissions_data_unittest.cc
@@ -175,7 +175,7 @@
       PermissionsData::GetPermissionMessageDetailsStrings(extension.get());
   ASSERT_EQ(1u, warnings.size());
   ASSERT_EQ(1u, warnings_details.size());
-  EXPECT_EQ("Access your data on 5 website(s)", UTF16ToUTF8(warnings[0]));
+  EXPECT_EQ("Access your data on 5 websites", UTF16ToUTF8(warnings[0]));
   EXPECT_EQ("- www.a.com\n- www.b.com\n- www.c.com\n- www.d.com\n- www.e.com",
             UTF16ToUTF8(warnings_details[0]));
 }
diff --git a/chrome/common/extensions/permissions/socket_permission.cc b/chrome/common/extensions/permissions/socket_permission.cc
index ebf7205..9f2c35c 100644
--- a/chrome/common/extensions/permissions/socket_permission.cc
+++ b/chrome/common/extensions/permissions/socket_permission.cc
@@ -37,8 +37,8 @@
 bool SocketPermission::AddAnyHostMessage(PermissionMessages& messages) const {
   std::set<SocketPermissionData>::const_iterator i;
   for (i = data_set_.begin(); i != data_set_.end(); ++i) {
-    if (i->IsAddressBoundType() &&
-        i->GetHostType() == SocketPermissionData::ANY_HOST) {
+    if (i->entry().IsAddressBoundType() &&
+        i->entry().GetHostType() == SocketPermissionEntry::ANY_HOST) {
       messages.push_back(PermissionMessage(
           PermissionMessage::kSocketAnyHost,
           l10n_util::GetStringUTF16(
@@ -54,8 +54,8 @@
   std::set<string16> domains;
   std::set<SocketPermissionData>::const_iterator i;
   for (i = data_set_.begin(); i != data_set_.end(); ++i) {
-    if (i->GetHostType() == SocketPermissionData::HOSTS_IN_DOMAINS)
-      domains.insert(UTF8ToUTF16(i->GetHost()));
+    if (i->entry().GetHostType() == SocketPermissionEntry::HOSTS_IN_DOMAINS)
+      domains.insert(UTF8ToUTF16(i->entry().pattern().host));
   }
   if (!domains.empty()) {
     int id = (domains.size() == 1) ?
@@ -76,8 +76,8 @@
   std::set<string16> hostnames;
   std::set<SocketPermissionData>::const_iterator i;
   for (i = data_set_.begin(); i != data_set_.end(); ++i) {
-    if (i->GetHostType() == SocketPermissionData::SPECIFIC_HOSTS)
-      hostnames.insert(UTF8ToUTF16(i->GetHost()));
+    if (i->entry().GetHostType() == SocketPermissionEntry::SPECIFIC_HOSTS)
+      hostnames.insert(UTF8ToUTF16(i->entry().pattern().host));
   }
   if (!hostnames.empty()) {
     int id = (hostnames.size() == 1) ?
@@ -97,7 +97,8 @@
     PermissionMessages& messages) const {
   std::set<SocketPermissionData>::const_iterator i;
   for (i = data_set_.begin(); i != data_set_.end(); ++i) {
-    if (i->pattern().type == content::SocketPermissionRequest::NETWORK_STATE) {
+    if (i->entry().pattern().type ==
+        content::SocketPermissionRequest::NETWORK_STATE) {
       messages.push_back(PermissionMessage(
           PermissionMessage::kNetworkState,
           l10n_util::GetStringUTF16(
diff --git a/chrome/common/extensions/permissions/socket_permission_data.cc b/chrome/common/extensions/permissions/socket_permission_data.cc
index fc7908e..e87fd84 100644
--- a/chrome/common/extensions/permissions/socket_permission_data.cc
+++ b/chrome/common/extensions/permissions/socket_permission_data.cc
@@ -23,8 +23,6 @@
 using extensions::SocketPermissionData;
 
 const char kColon = ':';
-const char kDot = '.';
-const char kWildcard[] = "*";
 const char kInvalid[] = "invalid";
 const char kTCPConnect[] = "tcp-connect";
 const char kTCPListen[] = "tcp-listen";
@@ -34,8 +32,6 @@
 const char kResolveHost[] = "resolve-host";
 const char kResolveProxy[] = "resolve-proxy";
 const char kNetworkState[] = "network-state";
-const int kWildcardPortNumber = 0;
-const int kInvalidPort = -1;
 
 SocketPermissionRequest::OperationType StringToType(const std::string& s) {
   if (s == kTCPConnect)
@@ -80,52 +76,20 @@
   }
 }
 
-bool StartsOrEndsWithWhitespace(const std::string& str) {
-  if (str.find_first_not_of(kWhitespaceASCII) != 0)
-    return true;
-  if (str.find_last_not_of(kWhitespaceASCII) != str.length() - 1)
-    return true;
-  return false;
-}
-
 }  // namespace
 
 namespace extensions {
 
-SocketPermissionData::SocketPermissionData()
-  : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort) {
-  Reset();
-}
+SocketPermissionData::SocketPermissionData() { }
 
-SocketPermissionData::~SocketPermissionData() {
-}
+SocketPermissionData::~SocketPermissionData() { }
 
 bool SocketPermissionData::operator<(const SocketPermissionData& rhs) const {
-  if (pattern_.type < rhs.pattern_.type)
-    return true;
-  if (pattern_.type > rhs.pattern_.type)
-    return false;
-
-  if (pattern_.host < rhs.pattern_.host)
-    return true;
-  if (pattern_.host > rhs.pattern_.host)
-    return false;
-
-  if (match_subdomains_ < rhs.match_subdomains_)
-    return true;
-  if (match_subdomains_ > rhs.match_subdomains_)
-    return false;
-
-  if (pattern_.port < rhs.pattern_.port)
-    return true;
-  return false;
+  return entry_ < rhs.entry_;
 }
 
 bool SocketPermissionData::operator==(const SocketPermissionData& rhs) const {
-  return (pattern_.type == rhs.pattern_.type) &&
-         (pattern_.host == rhs.pattern_.host) &&
-         (match_subdomains_ == rhs.match_subdomains_) &&
-         (pattern_.port == rhs.pattern_.port);
+  return entry_ == rhs.entry_;
 }
 
 bool SocketPermissionData::Check(
@@ -136,41 +100,7 @@
       *static_cast<const SocketPermission::CheckParam*>(param);
   const SocketPermissionRequest &request = specific_param.request;
 
-  if (pattern_.type != request.type)
-    return false;
-
-  std::string lhost = StringToLowerASCII(request.host);
-  if (pattern_.host != lhost) {
-    if (!match_subdomains_)
-      return false;
-
-    if (!pattern_.host.empty()) {
-      // Do not wildcard part of IP address.
-      url_parse::Component component(0, lhost.length());
-      url_canon::RawCanonOutputT<char, 128> ignored_output;
-      url_canon::CanonHostInfo host_info;
-      url_canon::CanonicalizeIPAddress(lhost.c_str(), component,
-                                       &ignored_output, &host_info);
-      if (host_info.IsIPAddress())
-        return false;
-
-      // host should equal one or more chars + "." +  host_.
-      int i = lhost.length() - pattern_.host.length();
-      if (i < 2)
-        return false;
-
-      if (lhost.compare(i, pattern_.host.length(), pattern_.host) != 0)
-        return false;
-
-      if (lhost[i - 1] != kDot)
-        return false;
-    }
-  }
-
-  if (pattern_.port != request.port && pattern_.port != kWildcardPortNumber)
-    return false;
-
-  return true;
+  return entry_.Check(request);
 }
 
 scoped_ptr<base::Value> SocketPermissionData::ToValue() const {
@@ -185,95 +115,27 @@
   return Parse(spec);
 }
 
-bool SocketPermissionData::IsAddressBoundType() const {
-  return pattern_.type == SocketPermissionRequest::TCP_CONNECT ||
-      pattern_.type == SocketPermissionRequest::TCP_LISTEN ||
-      pattern_.type == SocketPermissionRequest::UDP_BIND ||
-      pattern_.type == SocketPermissionRequest::UDP_SEND_TO;
-}
-
-SocketPermissionData::HostType SocketPermissionData::GetHostType() const {
-  return pattern_.host.empty() ? SocketPermissionData::ANY_HOST :
-         match_subdomains_     ? SocketPermissionData::HOSTS_IN_DOMAINS :
-                                 SocketPermissionData::SPECIFIC_HOSTS;
-}
-
-const std::string SocketPermissionData::GetHost() const {
-  return pattern_.host;
-}
-
-content::SocketPermissionRequest& SocketPermissionData::pattern() {
-  // Clear the spec because the caller could mutate |this|.
-  spec_.clear();
-  return pattern_;
-}
-
-bool& SocketPermissionData::match_subdomains() {
-  // Clear the spec because the caller could mutate |this|.
-  spec_.clear();
-  return match_subdomains_;
+SocketPermissionEntry& SocketPermissionData::entry() {
+   // Clear the spec because the caller could mutate |this|.
+   spec_.clear();
+  return entry_;
 }
 
 // TODO(ikarienator): Rewrite this method to support IPv6.
 bool SocketPermissionData::Parse(const std::string& permission) {
-  do {
-    pattern_.host.clear();
-    match_subdomains_ = true;
-    pattern_.port = kWildcardPortNumber;
-    spec_.clear();
-
-    std::vector<std::string> tokens;
-    base::SplitStringDontTrim(permission, kColon, &tokens);
-
-    if (tokens.empty() || tokens.size() > 3)
-      break;
-
-    pattern_.type = StringToType(tokens[0]);
-    if (pattern_.type == SocketPermissionRequest::NONE)
-      break;
-
-    if (tokens.size() == 1)
-      return true;
-
-    // Return an error if address is specified for permissions that don't
-    // need it (such as 'resolve-host').
-    if (!IsAddressBoundType())
-      break;
-
-    pattern_.host = tokens[1];
-    if (!pattern_.host.empty()) {
-      if (StartsOrEndsWithWhitespace(pattern_.host))
-        break;
-      pattern_.host = StringToLowerASCII(pattern_.host);
-
-      // The first component can optionally be '*' to match all subdomains.
-      std::vector<std::string> host_components;
-      base::SplitString(pattern_.host, kDot, &host_components);
-      DCHECK(!host_components.empty());
-
-      if (host_components[0] == kWildcard || host_components[0].empty()) {
-        host_components.erase(host_components.begin(),
-                              host_components.begin() + 1);
-      } else {
-        match_subdomains_ = false;
-      }
-      pattern_.host = JoinString(host_components, kDot);
-    }
-
-    if (tokens.size() == 2 || tokens[2].empty() || tokens[2] == kWildcard)
-      return true;
-
-    if (StartsOrEndsWithWhitespace(tokens[2]))
-      break;
-
-    if (!base::StringToInt(tokens[2], &pattern_.port) ||
-        pattern_.port < 1 || pattern_.port > 65535)
-      break;
-    return true;
-  } while (false);
-
   Reset();
-  return false;
+
+  std::vector<std::string> tokens;
+  base::SplitStringDontTrim(permission, kColon, &tokens);
+  if (tokens.empty())
+    return false;
+
+  SocketPermissionRequest::OperationType type = StringToType(tokens[0]);
+  if (type == SocketPermissionRequest::NONE)
+    return false;
+
+  tokens.erase(tokens.begin());
+  return SocketPermissionEntry::ParseHostPattern(type, tokens, &entry_);
 }
 
 const std::string& SocketPermissionData::GetAsString() const {
@@ -281,32 +143,16 @@
     return spec_;
 
   spec_.reserve(64);
-  spec_.append(TypeToString(pattern_.type));
-
-  if (!IsAddressBoundType())
-    return spec_;
-
-  if (match_subdomains_) {
-    spec_.append(1, kColon).append(kWildcard);
-    if (!pattern_.host.empty())
-      spec_.append(1, kDot).append(pattern_.host);
-  } else {
-     spec_.append(1, kColon).append(pattern_.host);
+  spec_.append(TypeToString(entry_.pattern().type));
+  std::string pattern = entry_.GetHostPatternAsString();
+  if (!pattern.empty()) {
+    spec_.append(1, kColon).append(pattern);
   }
-
-  if (pattern_.port == kWildcardPortNumber)
-    spec_.append(1, kColon).append(kWildcard);
-  else
-    spec_.append(1, kColon).append(base::IntToString(pattern_.port));
-
   return spec_;
 }
 
 void SocketPermissionData::Reset() {
-  pattern_.type = SocketPermissionRequest::NONE;
-  pattern_.host.clear();
-  match_subdomains_ = false;
-  pattern_.port = kInvalidPort;
+  entry_ = SocketPermissionEntry();
   spec_.clear();
 }
 
diff --git a/chrome/common/extensions/permissions/socket_permission_data.h b/chrome/common/extensions/permissions/socket_permission_data.h
index 1a23cc2..d647568 100644
--- a/chrome/common/extensions/permissions/socket_permission_data.h
+++ b/chrome/common/extensions/permissions/socket_permission_data.h
@@ -6,9 +6,11 @@
 
 #include <string>
 
-#include "base/memory/scoped_ptr.h"
 #include "chrome/common/extensions/permissions/api_permission.h"
-#include "content/public/common/socket_permission_request.h"
+#include "chrome/common/extensions/permissions/socket_permission_entry.h"
+#include "ipc/ipc_param_traits.h"
+
+template <class T> struct FuzzTraits;
 
 namespace extensions {
 
@@ -35,12 +37,6 @@
 // The multicast membership permission implies a permission to any address.
 class SocketPermissionData {
  public:
-  enum HostType {
-    ANY_HOST,
-    HOSTS_IN_DOMAINS,
-    SPECIFIC_HOSTS,
-  };
-
   SocketPermissionData();
   ~SocketPermissionData();
 
@@ -59,33 +55,26 @@
   // Populate |this| from a base::Value.
   bool FromValue(const base::Value* value);
 
-  // Returns true if the permission type can be bound to a host or port.
-  bool IsAddressBoundType() const;
-
-  HostType GetHostType() const;
-  const std::string GetHost() const;
-
-  const content::SocketPermissionRequest& pattern() const { return pattern_; }
-  const bool& match_subdomains() const { return match_subdomains_; }
-
-  // These accessors are provided for IPC_STRUCT_TRAITS_MEMBER.  Please
-  // think twice before using them for anything else.
-  content::SocketPermissionRequest& pattern();
-  bool& match_subdomains();
-
   // TODO(bryeung): SocketPermissionData should be encoded as a base::Value
   // instead of a string.  Until that is done, expose these methods for
   // testing.
   bool ParseForTest(const std::string& permission) { return Parse(permission); }
   const std::string& GetAsStringForTest() const { return GetAsString(); }
 
+  const SocketPermissionEntry& entry() const { return entry_; }
+
  private:
+  // Friend so ParamTraits can serialize us.
+  friend struct IPC::ParamTraits<SocketPermissionData>;
+  friend struct FuzzTraits<SocketPermissionData>;
+
+  SocketPermissionEntry& entry();
+
   bool Parse(const std::string& permission);
   const std::string& GetAsString() const;
   void Reset();
 
-  content::SocketPermissionRequest pattern_;
-  bool match_subdomains_;
+  SocketPermissionEntry entry_;
   mutable std::string spec_;
 };
 
diff --git a/chrome/common/extensions/permissions/socket_permission_entry.cc b/chrome/common/extensions/permissions/socket_permission_entry.cc
new file mode 100644
index 0000000..17f2ef7
--- /dev/null
+++ b/chrome/common/extensions/permissions/socket_permission_entry.cc
@@ -0,0 +1,227 @@
+// Copyright 2013 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 "chrome/common/extensions/permissions/socket_permission_entry.h"
+
+#include <cstdlib>
+#include <sstream>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "chrome/common/extensions/permissions/api_permission.h"
+#include "chrome/common/extensions/permissions/socket_permission.h"
+#include "url/url_canon.h"
+
+namespace {
+
+using content::SocketPermissionRequest;
+
+const char kColon = ':';
+const char kDot = '.';
+const char kWildcard[] = "*";
+const int kWildcardPortNumber = 0;
+const int kInvalidPort = -1;
+
+bool StartsOrEndsWithWhitespace(const std::string& str) {
+  if (str.find_first_not_of(kWhitespaceASCII) != 0)
+    return true;
+  if (str.find_last_not_of(kWhitespaceASCII) != str.length() - 1)
+    return true;
+  return false;
+}
+
+}  // namespace
+
+namespace extensions {
+
+SocketPermissionEntry::SocketPermissionEntry()
+  : pattern_(SocketPermissionRequest::NONE, std::string(), kInvalidPort),
+    match_subdomains_(false) {
+}
+
+SocketPermissionEntry::~SocketPermissionEntry() {}
+
+bool SocketPermissionEntry::operator<(const SocketPermissionEntry& rhs) const {
+  if (pattern_.type < rhs.pattern_.type)
+    return true;
+  if (pattern_.type > rhs.pattern_.type)
+    return false;
+
+  if (pattern_.host < rhs.pattern_.host)
+    return true;
+  if (pattern_.host > rhs.pattern_.host)
+    return false;
+
+  if (match_subdomains_ < rhs.match_subdomains_)
+    return true;
+  if (match_subdomains_ > rhs.match_subdomains_)
+    return false;
+
+  if (pattern_.port < rhs.pattern_.port)
+    return true;
+  return false;
+}
+
+bool SocketPermissionEntry::operator==(const SocketPermissionEntry& rhs) const {
+  return (pattern_.type == rhs.pattern_.type) &&
+         (pattern_.host == rhs.pattern_.host) &&
+         (match_subdomains_ == rhs.match_subdomains_) &&
+         (pattern_.port == rhs.pattern_.port);
+}
+
+bool SocketPermissionEntry::Check(
+    const content::SocketPermissionRequest& request) const {
+  if (pattern_.type != request.type)
+    return false;
+
+  std::string lhost = StringToLowerASCII(request.host);
+  if (pattern_.host != lhost) {
+    if (!match_subdomains_)
+      return false;
+
+    if (!pattern_.host.empty()) {
+      // Do not wildcard part of IP address.
+      url_parse::Component component(0, lhost.length());
+      url_canon::RawCanonOutputT<char, 128> ignored_output;
+      url_canon::CanonHostInfo host_info;
+      url_canon::CanonicalizeIPAddress(lhost.c_str(), component,
+                                       &ignored_output, &host_info);
+      if (host_info.IsIPAddress())
+        return false;
+
+      // host should equal one or more chars + "." +  host_.
+      int i = lhost.length() - pattern_.host.length();
+      if (i < 2)
+        return false;
+
+      if (lhost.compare(i, pattern_.host.length(), pattern_.host) != 0)
+        return false;
+
+      if (lhost[i - 1] != kDot)
+        return false;
+    }
+  }
+
+  if (pattern_.port != request.port && pattern_.port != kWildcardPortNumber)
+    return false;
+
+  return true;
+}
+
+SocketPermissionEntry::HostType SocketPermissionEntry::GetHostType() const {
+  return pattern_.host.empty() ? SocketPermissionEntry::ANY_HOST :
+         match_subdomains_     ? SocketPermissionEntry::HOSTS_IN_DOMAINS :
+                                 SocketPermissionEntry::SPECIFIC_HOSTS;
+}
+
+bool SocketPermissionEntry::IsAddressBoundType() const {
+  return pattern_.type == SocketPermissionRequest::TCP_CONNECT ||
+      pattern_.type == SocketPermissionRequest::TCP_LISTEN ||
+      pattern_.type == SocketPermissionRequest::UDP_BIND ||
+      pattern_.type == SocketPermissionRequest::UDP_SEND_TO;
+}
+
+// static
+bool SocketPermissionEntry::ParseHostPattern(
+    SocketPermissionRequest::OperationType type,
+    const std::string& pattern,
+    SocketPermissionEntry* entry) {
+  std::vector<std::string> tokens;
+  base::SplitStringDontTrim(pattern, kColon, &tokens);
+  return ParseHostPattern(type, tokens, entry);
+}
+
+// static
+bool SocketPermissionEntry::ParseHostPattern(
+    SocketPermissionRequest::OperationType type,
+    const std::vector<std::string>& pattern_tokens,
+    SocketPermissionEntry* entry) {
+
+  SocketPermissionEntry result;
+
+  if (type == SocketPermissionRequest::NONE)
+    return false;
+
+  if (pattern_tokens.size() > 2)
+    return false;
+
+  result.pattern_.type = type;
+  result.pattern_.port = kWildcardPortNumber;
+  result.match_subdomains_ = true;
+
+  if (pattern_tokens.size() == 0) {
+    *entry = result;
+    return true;
+  }
+
+    // Return an error if address is specified for permissions that don't
+    // need it (such as 'resolve-host').
+    if (!result.IsAddressBoundType())
+      return false;
+
+  result.pattern_.host = pattern_tokens[0];
+  if (!result.pattern_.host.empty()) {
+    if (StartsOrEndsWithWhitespace(result.pattern_.host))
+      return false;
+    result.pattern_.host = StringToLowerASCII(result.pattern_.host);
+
+    // The first component can optionally be '*' to match all subdomains.
+    std::vector<std::string> host_components;
+    base::SplitString(result.pattern_.host, kDot, &host_components);
+    DCHECK(!host_components.empty());
+
+    if (host_components[0] == kWildcard || host_components[0].empty()) {
+      host_components.erase(host_components.begin(),
+                            host_components.begin() + 1);
+    } else {
+      result.match_subdomains_ = false;
+    }
+    result.pattern_.host = JoinString(host_components, kDot);
+  }
+
+  if (pattern_tokens.size() == 1 ||
+      pattern_tokens[1].empty() ||
+      pattern_tokens[1] == kWildcard) {
+    *entry = result;
+    return true;
+  }
+
+  if (StartsOrEndsWithWhitespace(pattern_tokens[1]))
+    return false;
+
+  if (!base::StringToInt(pattern_tokens[1], &result.pattern_.port) ||
+      result.pattern_.port < 1 || result.pattern_.port > 65535)
+    return false;
+
+  *entry = result;
+  return true;
+}
+
+std::string SocketPermissionEntry::GetHostPatternAsString() const {
+  std::string result;
+
+  if (!IsAddressBoundType())
+    return result;
+
+  if (match_subdomains()) {
+    result.append(kWildcard);
+    if (!pattern_.host.empty())
+      result.append(1, kDot).append(pattern_.host);
+  } else {
+     result.append(pattern_.host);
+  }
+
+  if (pattern_.port == kWildcardPortNumber)
+    result.append(1, kColon).append(kWildcard);
+  else
+    result.append(1, kColon).append(base::IntToString(pattern_.port));
+
+  return result;
+}
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/permissions/socket_permission_entry.h b/chrome/common/extensions/permissions/socket_permission_entry.h
new file mode 100644
index 0000000..b656471
--- /dev/null
+++ b/chrome/common/extensions/permissions/socket_permission_entry.h
@@ -0,0 +1,82 @@
+// Copyright 2013 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 CHROME_COMMON_EXTENSIONS_PERMISSIONS_SOCKET_PERMISSION_ENTRY_H_
+#define CHROME_COMMON_EXTENSIONS_PERMISSIONS_SOCKET_PERMISSION_ENTRY_H_
+
+#include <string>
+#include <vector>
+
+#include "content/public/common/socket_permission_request.h"
+#include "ipc/ipc_param_traits.h"
+
+template <class T> struct FuzzTraits;
+
+namespace extensions {
+
+// Internal representation of a socket permission for a specific operation, such
+// as UDP "bind", host 127.0.0.1, port *.
+class SocketPermissionEntry {
+ public:
+  enum HostType {
+    ANY_HOST,
+    HOSTS_IN_DOMAINS,
+    SPECIFIC_HOSTS,
+  };
+
+  SocketPermissionEntry();
+  ~SocketPermissionEntry();
+
+  // operators <, == are needed by container std::set and algorithms
+  // std::set_includes and std::set_differences.
+  bool operator<(const SocketPermissionEntry& rhs) const;
+  bool operator==(const SocketPermissionEntry& rhs) const;
+
+  bool Check(const content::SocketPermissionRequest& request) const;
+
+  // Parse a host:port pattern for a given operation type.
+  //   <pattern> := '' |
+  //                <host> |
+  //                ':' <port> |
+  //                <host> ':' <port> |
+  //
+  //   <host> := '*' |
+  //             '*.' <anychar except '/' and '*'>+ |
+  //             <anychar except '/' and '*'>+
+  //
+  //   <port> := '*' |
+  //             <port number between 0 and 65535>)
+  static bool ParseHostPattern(
+      content::SocketPermissionRequest::OperationType type,
+      const std::string& pattern,
+      SocketPermissionEntry* entry);
+
+  static bool ParseHostPattern(
+      content::SocketPermissionRequest::OperationType type,
+      const std::vector<std::string>& pattern_tokens,
+      SocketPermissionEntry* entry);
+
+  // Returns true if the permission type can be bound to a host or port.
+  bool IsAddressBoundType() const;
+
+  std::string GetHostPatternAsString() const;
+  HostType GetHostType() const;
+
+  const content::SocketPermissionRequest& pattern() const { return pattern_; }
+  bool match_subdomains() const { return match_subdomains_; }
+
+ private:
+  // Friend so ParamTraits can serialize us.
+  friend struct IPC::ParamTraits<SocketPermissionEntry>;
+  friend struct FuzzTraits<SocketPermissionEntry>;
+
+  // The permission type, host and port.
+  content::SocketPermissionRequest pattern_;
+
+  // True if there was a wildcard in the host name.
+  bool match_subdomains_;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_COMMON_EXTENSIONS_PERMISSIONS_SOCKET_PERMISSION_ENTRY_H_