Fix sort order of unlaunched apps on app list start page.
[chromium-blink-merge.git] / extensions / common / manifest_handlers / externally_connectable.cc
blob1314c6d1cb0fa47b699aecf626353956489601d0
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/common/manifest_handlers/externally_connectable.h"
7 #include <algorithm>
9 #include "base/stl_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/crx_file/id_util.h"
12 #include "extensions/common/api/extensions_manifest_types.h"
13 #include "extensions/common/error_utils.h"
14 #include "extensions/common/manifest_constants.h"
15 #include "extensions/common/manifest_handlers/permissions_parser.h"
16 #include "extensions/common/permissions/api_permission_set.h"
17 #include "extensions/common/url_pattern.h"
18 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
19 #include "url/gurl.h"
21 namespace rcd = net::registry_controlled_domains;
23 namespace extensions {
25 namespace externally_connectable_errors {
26 const char kErrorInvalidMatchPattern[] = "Invalid match pattern '*'";
27 const char kErrorInvalidId[] = "Invalid ID '*'";
28 const char kErrorNothingSpecified[] =
29 "'externally_connectable' specifies neither 'matches' nor 'ids'; "
30 "nothing will be able to connect";
31 const char kErrorTopLevelDomainsNotAllowed[] =
32 "\"*\" is an effective top level domain for which wildcard subdomains such "
33 "as \"*\" are not allowed";
34 const char kErrorWildcardHostsNotAllowed[] =
35 "Wildcard domain patterns such as \"*\" are not allowed";
36 } // namespace externally_connectable_errors
38 namespace keys = extensions::manifest_keys;
39 namespace errors = externally_connectable_errors;
40 using core_api::extensions_manifest_types::ExternallyConnectable;
42 namespace {
44 const char kAllIds[] = "*";
46 template <typename T>
47 std::vector<T> Sorted(const std::vector<T>& in) {
48 std::vector<T> out = in;
49 std::sort(out.begin(), out.end());
50 return out;
53 } // namespace
55 ExternallyConnectableHandler::ExternallyConnectableHandler() {
58 ExternallyConnectableHandler::~ExternallyConnectableHandler() {
61 bool ExternallyConnectableHandler::Parse(Extension* extension,
62 base::string16* error) {
63 const base::Value* externally_connectable = NULL;
64 CHECK(extension->manifest()->Get(keys::kExternallyConnectable,
65 &externally_connectable));
66 bool allow_all_urls = PermissionsParser::HasAPIPermission(
67 extension, APIPermission::kExternallyConnectableAllUrls);
69 std::vector<InstallWarning> install_warnings;
70 scoped_ptr<ExternallyConnectableInfo> info =
71 ExternallyConnectableInfo::FromValue(
72 *externally_connectable, allow_all_urls, &install_warnings, error);
73 if (!info)
74 return false;
75 if (!info->matches.is_empty()) {
76 PermissionsParser::AddAPIPermission(extension,
77 APIPermission::kWebConnectable);
79 extension->AddInstallWarnings(install_warnings);
80 extension->SetManifestData(keys::kExternallyConnectable, info.release());
81 return true;
84 const std::vector<std::string> ExternallyConnectableHandler::Keys() const {
85 return SingleKey(keys::kExternallyConnectable);
88 // static
89 ExternallyConnectableInfo* ExternallyConnectableInfo::Get(
90 const Extension* extension) {
91 return static_cast<ExternallyConnectableInfo*>(
92 extension->GetManifestData(keys::kExternallyConnectable));
95 // static
96 scoped_ptr<ExternallyConnectableInfo> ExternallyConnectableInfo::FromValue(
97 const base::Value& value,
98 bool allow_all_urls,
99 std::vector<InstallWarning>* install_warnings,
100 base::string16* error) {
101 scoped_ptr<ExternallyConnectable> externally_connectable =
102 ExternallyConnectable::FromValue(value, error);
103 if (!externally_connectable)
104 return scoped_ptr<ExternallyConnectableInfo>();
106 URLPatternSet matches;
108 if (externally_connectable->matches) {
109 for (std::vector<std::string>::iterator it =
110 externally_connectable->matches->begin();
111 it != externally_connectable->matches->end();
112 ++it) {
113 // Safe to use SCHEME_ALL here; externally_connectable gives a page ->
114 // extension communication path, not the other way.
115 URLPattern pattern(URLPattern::SCHEME_ALL);
116 if (pattern.Parse(*it) != URLPattern::PARSE_SUCCESS) {
117 *error = ErrorUtils::FormatErrorMessageUTF16(
118 errors::kErrorInvalidMatchPattern, *it);
119 return scoped_ptr<ExternallyConnectableInfo>();
122 if (allow_all_urls && pattern.match_all_urls()) {
123 matches.AddPattern(pattern);
124 continue;
127 // Wildcard hosts are not allowed.
128 if (pattern.host().empty()) {
129 // Warning not error for forwards compatibility.
130 install_warnings->push_back(
131 InstallWarning(ErrorUtils::FormatErrorMessage(
132 errors::kErrorWildcardHostsNotAllowed, *it),
133 keys::kExternallyConnectable,
134 *it));
135 continue;
138 // Wildcards on subdomains of a TLD are not allowed.
139 size_t registry_length = rcd::GetRegistryLength(
140 pattern.host(),
141 // This means that things that look like TLDs - the foobar in
142 // http://google.foobar - count as TLDs.
143 rcd::INCLUDE_UNKNOWN_REGISTRIES,
144 // This means that effective TLDs like appspot.com count as TLDs;
145 // codereview.appspot.com and evil.appspot.com are different.
146 rcd::INCLUDE_PRIVATE_REGISTRIES);
148 if (registry_length == std::string::npos) {
149 // The URL parsing combined with host().empty() should have caught this.
150 NOTREACHED() << *it;
151 *error = ErrorUtils::FormatErrorMessageUTF16(
152 errors::kErrorInvalidMatchPattern, *it);
153 return scoped_ptr<ExternallyConnectableInfo>();
156 // Broad match patterns like "*.com", "*.co.uk", and even "*.appspot.com"
157 // are not allowed. However just "appspot.com" is ok.
158 if (registry_length == 0 && pattern.match_subdomains()) {
159 // Warning not error for forwards compatibility.
160 install_warnings->push_back(
161 InstallWarning(ErrorUtils::FormatErrorMessage(
162 errors::kErrorTopLevelDomainsNotAllowed,
163 pattern.host().c_str(),
164 *it),
165 keys::kExternallyConnectable,
166 *it));
167 continue;
170 matches.AddPattern(pattern);
174 std::vector<std::string> ids;
175 bool all_ids = false;
177 if (externally_connectable->ids) {
178 for (std::vector<std::string>::iterator it =
179 externally_connectable->ids->begin();
180 it != externally_connectable->ids->end();
181 ++it) {
182 if (*it == kAllIds) {
183 all_ids = true;
184 } else if (crx_file::id_util::IdIsValid(*it)) {
185 ids.push_back(*it);
186 } else {
187 *error =
188 ErrorUtils::FormatErrorMessageUTF16(errors::kErrorInvalidId, *it);
189 return scoped_ptr<ExternallyConnectableInfo>();
194 if (!externally_connectable->matches && !externally_connectable->ids) {
195 install_warnings->push_back(InstallWarning(errors::kErrorNothingSpecified,
196 keys::kExternallyConnectable));
199 bool accepts_tls_channel_id =
200 externally_connectable->accepts_tls_channel_id.get() &&
201 *externally_connectable->accepts_tls_channel_id;
202 return make_scoped_ptr(new ExternallyConnectableInfo(
203 matches, ids, all_ids, accepts_tls_channel_id));
206 ExternallyConnectableInfo::~ExternallyConnectableInfo() {
209 ExternallyConnectableInfo::ExternallyConnectableInfo(
210 const URLPatternSet& matches,
211 const std::vector<std::string>& ids,
212 bool all_ids,
213 bool accepts_tls_channel_id)
214 : matches(matches),
215 ids(Sorted(ids)),
216 all_ids(all_ids),
217 accepts_tls_channel_id(accepts_tls_channel_id) {
220 bool ExternallyConnectableInfo::IdCanConnect(const std::string& id) {
221 if (all_ids)
222 return true;
223 DCHECK(base::STLIsSorted(ids));
224 return std::binary_search(ids.begin(), ids.end(), id);
227 } // namespace extensions