Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(450)

Side by Side Diff: chrome/common/extensions/features/feature.cc

Issue 11316164: Implement ComplexFeature to support permission features with multiple rules. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Renames and add TODO Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/common/extensions/features/feature.h" 5 #include "chrome/common/extensions/features/feature.h"
6 6
7 #include <map> 7 #include <map>
8 8
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/lazy_instance.h" 10 #include "base/lazy_instance.h"
11 #include "base/stringprintf.h" 11 #include "base/stringprintf.h"
12 #include "base/string_util.h" 12 #include "base/string_util.h"
13 #include "chrome/common/chrome_switches.h" 13 #include "chrome/common/chrome_switches.h"
14 14
15 using chrome::VersionInfo; 15 using chrome::VersionInfo;
16 using extensions::Extension; 16 using extensions::Extension;
17 17
18 namespace { 18 namespace {
19 19
20 struct Mappings {
21 Mappings() {
22 extension_types["extension"] = Extension::TYPE_EXTENSION;
23 extension_types["theme"] = Extension::TYPE_THEME;
24 extension_types["packaged_app"]
25 = Extension::TYPE_LEGACY_PACKAGED_APP;
26 extension_types["hosted_app"] = Extension::TYPE_HOSTED_APP;
27 extension_types["platform_app"] = Extension::TYPE_PLATFORM_APP;
28
29 contexts["blessed_extension"] =
30 extensions::Feature::BLESSED_EXTENSION_CONTEXT;
31 contexts["unblessed_extension"] =
32 extensions::Feature::UNBLESSED_EXTENSION_CONTEXT;
33 contexts["content_script"] = extensions::Feature::CONTENT_SCRIPT_CONTEXT;
34 contexts["web_page"] = extensions::Feature::WEB_PAGE_CONTEXT;
35
36 locations["component"] = extensions::Feature::COMPONENT_LOCATION;
37
38 platforms["chromeos"] = extensions::Feature::CHROMEOS_PLATFORM;
39
40 channels["trunk"] = VersionInfo::CHANNEL_UNKNOWN;
41 channels["canary"] = VersionInfo::CHANNEL_CANARY;
42 channels["dev"] = VersionInfo::CHANNEL_DEV;
43 channels["beta"] = VersionInfo::CHANNEL_BETA;
44 channels["stable"] = VersionInfo::CHANNEL_STABLE;
45 }
46
47 std::map<std::string, Extension::Type> extension_types;
48 std::map<std::string, extensions::Feature::Context> contexts;
49 std::map<std::string, extensions::Feature::Location> locations;
50 std::map<std::string, extensions::Feature::Platform> platforms;
51 std::map<std::string, VersionInfo::Channel> channels;
52 };
53
54 base::LazyInstance<Mappings> g_mappings = LAZY_INSTANCE_INITIALIZER;
55
56 std::string GetChannelName(VersionInfo::Channel channel) {
57 typedef std::map<std::string, VersionInfo::Channel> ChannelsMap;
58 ChannelsMap channels = g_mappings.Get().channels;
59 for (ChannelsMap::iterator i = channels.begin(); i != channels.end(); ++i) {
60 if (i->second == channel)
61 return i->first;
62 }
63 NOTREACHED();
64 return "unknown";
65 }
66
67 const VersionInfo::Channel kDefaultChannel = VersionInfo::CHANNEL_STABLE; 20 const VersionInfo::Channel kDefaultChannel = VersionInfo::CHANNEL_STABLE;
68 VersionInfo::Channel g_current_channel = kDefaultChannel; 21 VersionInfo::Channel g_current_channel = kDefaultChannel;
69 22
70 // TODO(aa): Can we replace all this manual parsing with JSON schema stuff?
71
72 void ParseSet(const DictionaryValue* value,
73 const std::string& property,
74 std::set<std::string>* set) {
75 const ListValue* list_value = NULL;
76 if (!value->GetList(property, &list_value))
77 return;
78
79 set->clear();
80 for (size_t i = 0; i < list_value->GetSize(); ++i) {
81 std::string str_val;
82 CHECK(list_value->GetString(i, &str_val)) << property << " " << i;
83 set->insert(str_val);
84 }
85 }
86
87 template<typename T>
88 void ParseEnum(const std::string& string_value,
89 T* enum_value,
90 const std::map<std::string, T>& mapping) {
91 typename std::map<std::string, T>::const_iterator iter =
92 mapping.find(string_value);
93 CHECK(iter != mapping.end()) << string_value;
94 *enum_value = iter->second;
95 }
96
97 template<typename T>
98 void ParseEnum(const DictionaryValue* value,
99 const std::string& property,
100 T* enum_value,
101 const std::map<std::string, T>& mapping) {
102 std::string string_value;
103 if (!value->GetString(property, &string_value))
104 return;
105
106 ParseEnum(string_value, enum_value, mapping);
107 }
108
109 template<typename T>
110 void ParseEnumSet(const DictionaryValue* value,
111 const std::string& property,
112 std::set<T>* enum_set,
113 const std::map<std::string, T>& mapping) {
114 if (!value->HasKey(property))
115 return;
116
117 enum_set->clear();
118
119 std::string property_string;
120 if (value->GetString(property, &property_string)) {
121 if (property_string == "all") {
122 for (typename std::map<std::string, T>::const_iterator j =
123 mapping.begin(); j != mapping.end(); ++j) {
124 enum_set->insert(j->second);
125 }
126 }
127 return;
128 }
129
130 std::set<std::string> string_set;
131 ParseSet(value, property, &string_set);
132 for (std::set<std::string>::iterator iter = string_set.begin();
133 iter != string_set.end(); ++iter) {
134 T enum_value = static_cast<T>(0);
135 ParseEnum(*iter, &enum_value, mapping);
136 enum_set->insert(enum_value);
137 }
138 }
139
140 // Gets a human-readable name for the given extension type.
141 std::string GetDisplayTypeName(Extension::Type type) {
142 switch (type) {
143 case Extension::TYPE_UNKNOWN:
144 return "unknown";
145 case Extension::TYPE_EXTENSION:
146 return "extension";
147 case Extension::TYPE_HOSTED_APP:
148 return "hosted app";
149 case Extension::TYPE_LEGACY_PACKAGED_APP:
150 return "legacy packaged app";
151 case Extension::TYPE_PLATFORM_APP:
152 return "packaged app";
153 case Extension::TYPE_THEME:
154 return "theme";
155 case Extension::TYPE_USER_SCRIPT:
156 return "user script";
157 }
158
159 NOTREACHED();
160 return "";
161 }
162
163 } // namespace 23 } // namespace
164 24
165 namespace extensions { 25 namespace extensions {
166 26
167 Feature::Feature()
168 : location_(UNSPECIFIED_LOCATION),
169 platform_(UNSPECIFIED_PLATFORM),
170 min_manifest_version_(0),
171 max_manifest_version_(0),
172 channel_(VersionInfo::CHANNEL_UNKNOWN) {
173 }
174
175 Feature::Feature(const Feature& other)
176 : whitelist_(other.whitelist_),
177 extension_types_(other.extension_types_),
178 contexts_(other.contexts_),
179 location_(other.location_),
180 platform_(other.platform_),
181 min_manifest_version_(other.min_manifest_version_),
182 max_manifest_version_(other.max_manifest_version_),
183 channel_(other.channel_) {
184 }
185
186 Feature::~Feature() {
187 }
188
189 bool Feature::Equals(const Feature& other) const {
190 return whitelist_ == other.whitelist_ &&
191 extension_types_ == other.extension_types_ &&
192 contexts_ == other.contexts_ &&
193 location_ == other.location_ &&
194 platform_ == other.platform_ &&
195 min_manifest_version_ == other.min_manifest_version_ &&
196 max_manifest_version_ == other.max_manifest_version_ &&
197 channel_ == other.channel_;
198 }
199
200 // static 27 // static
201 Feature::Platform Feature::GetCurrentPlatform() { 28 Feature::Platform Feature::GetCurrentPlatform() {
202 #if defined(OS_CHROMEOS) 29 #if defined(OS_CHROMEOS)
203 return CHROMEOS_PLATFORM; 30 return CHROMEOS_PLATFORM;
204 #else 31 #else
205 return UNSPECIFIED_PLATFORM; 32 return UNSPECIFIED_PLATFORM;
206 #endif 33 #endif
207 } 34 }
208 35
209 // static 36 // static
210 Feature::Location Feature::ConvertLocation(Extension::Location location) { 37 Feature::Location Feature::ConvertLocation(Extension::Location location) {
211 if (location == Extension::COMPONENT) 38 if (location == Extension::COMPONENT)
212 return COMPONENT_LOCATION; 39 return COMPONENT_LOCATION;
213 else 40 else
214 return UNSPECIFIED_LOCATION; 41 return UNSPECIFIED_LOCATION;
215 } 42 }
216 43
217 void Feature::Parse(const DictionaryValue* value) {
218 ParseSet(value, "whitelist", &whitelist_);
219 ParseEnumSet<Extension::Type>(value, "extension_types", &extension_types_,
220 g_mappings.Get().extension_types);
221 ParseEnumSet<Context>(value, "contexts", &contexts_,
222 g_mappings.Get().contexts);
223 ParseEnum<Location>(value, "location", &location_,
224 g_mappings.Get().locations);
225 ParseEnum<Platform>(value, "platform", &platform_,
226 g_mappings.Get().platforms);
227 value->GetInteger("min_manifest_version", &min_manifest_version_);
228 value->GetInteger("max_manifest_version", &max_manifest_version_);
229 ParseEnum<VersionInfo::Channel>(
230 value, "channel", &channel_,
231 g_mappings.Get().channels);
232 }
233
234 Feature::Availability Feature::IsAvailableToManifest(
235 const std::string& extension_id,
236 Extension::Type type,
237 Location location,
238 int manifest_version,
239 Platform platform) const {
240 // Component extensions can access any feature.
241 if (location == COMPONENT_LOCATION)
242 return CreateAvailability(IS_AVAILABLE, type);
243
244 if (!whitelist_.empty()) {
245 if (whitelist_.find(extension_id) == whitelist_.end()) {
246 // TODO(aa): This is gross. There should be a better way to test the
247 // whitelist.
248 CommandLine* command_line = CommandLine::ForCurrentProcess();
249 if (!command_line->HasSwitch(switches::kWhitelistedExtensionID))
250 return CreateAvailability(NOT_FOUND_IN_WHITELIST, type);
251
252 std::string whitelist_switch_value =
253 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
254 switches::kWhitelistedExtensionID);
255 if (extension_id != whitelist_switch_value)
256 return CreateAvailability(NOT_FOUND_IN_WHITELIST, type);
257 }
258 }
259
260 if (!extension_types_.empty() &&
261 extension_types_.find(type) == extension_types_.end()) {
262 return CreateAvailability(INVALID_TYPE, type);
263 }
264
265 if (location_ != UNSPECIFIED_LOCATION && location_ != location)
266 return CreateAvailability(INVALID_LOCATION, type);
267
268 if (platform_ != UNSPECIFIED_PLATFORM && platform_ != platform)
269 return CreateAvailability(INVALID_PLATFORM, type);
270
271 if (min_manifest_version_ != 0 && manifest_version < min_manifest_version_)
272 return CreateAvailability(INVALID_MIN_MANIFEST_VERSION, type);
273
274 if (max_manifest_version_ != 0 && manifest_version > max_manifest_version_)
275 return CreateAvailability(INVALID_MAX_MANIFEST_VERSION, type);
276
277 if (channel_ < g_current_channel)
278 return CreateAvailability(UNSUPPORTED_CHANNEL, type);
279
280 return CreateAvailability(IS_AVAILABLE, type);
281 }
282
283 Feature::Availability Feature::IsAvailableToContext(
284 const Extension* extension,
285 Feature::Context context,
286 Feature::Platform platform) const {
287 Availability result = IsAvailableToManifest(
288 extension->id(),
289 extension->GetType(),
290 ConvertLocation(extension->location()),
291 extension->manifest_version(),
292 platform);
293 if (!result.is_available())
294 return result;
295
296 if (!contexts_.empty() &&
297 contexts_.find(context) == contexts_.end()) {
298 return CreateAvailability(INVALID_CONTEXT, extension->GetType());
299 }
300
301 return CreateAvailability(IS_AVAILABLE);
302 }
303
304 // static 44 // static
305 chrome::VersionInfo::Channel Feature::GetCurrentChannel() { 45 chrome::VersionInfo::Channel Feature::GetCurrentChannel() {
306 return g_current_channel; 46 return g_current_channel;
307 } 47 }
308 48
309 // static 49 // static
310 void Feature::SetCurrentChannel(VersionInfo::Channel channel) { 50 void Feature::SetCurrentChannel(VersionInfo::Channel channel) {
311 g_current_channel = channel; 51 g_current_channel = channel;
312 } 52 }
313 53
314 // static 54 // static
315 chrome::VersionInfo::Channel Feature::GetDefaultChannel() { 55 chrome::VersionInfo::Channel Feature::GetDefaultChannel() {
316 return kDefaultChannel; 56 return kDefaultChannel;
317 } 57 }
318 58
319 Feature::Availability Feature::CreateAvailability( 59 Feature::~Feature() {}
320 AvailabilityResult result) const {
321 return Availability(
322 result, GetAvailabilityMessage(result, Extension::TYPE_UNKNOWN));
323 }
324
325 Feature::Availability Feature::CreateAvailability(
326 AvailabilityResult result, Extension::Type type) const {
327 return Availability(result, GetAvailabilityMessage(result, type));
328 }
329
330 std::string Feature::GetAvailabilityMessage(
331 AvailabilityResult result, Extension::Type type) const {
332 switch (result) {
333 case IS_AVAILABLE:
334 return "";
335 case NOT_FOUND_IN_WHITELIST:
336 return base::StringPrintf(
337 "'%s' is not allowed for specified extension ID.",
338 name().c_str());
339 case INVALID_TYPE: {
340 std::string allowed_type_names;
341 // Turn the set of allowed types into a vector so that it's easier to
342 // inject the appropriate separator into the display string.
343 std::vector<Extension::Type> extension_types(
344 extension_types_.begin(), extension_types_.end());
345 for (size_t i = 0; i < extension_types.size(); i++) {
346 // Pluralize type name.
347 allowed_type_names += GetDisplayTypeName(extension_types[i]) + "s";
348 if (i == extension_types_.size() - 2) {
349 allowed_type_names += " and ";
350 } else if (i != extension_types_.size() - 1) {
351 allowed_type_names += ", ";
352 }
353 }
354
355 return base::StringPrintf(
356 "'%s' is only allowed for %s, and this is a %s.",
357 name().c_str(),
358 allowed_type_names.c_str(),
359 GetDisplayTypeName(type).c_str());
360 }
361 case INVALID_CONTEXT:
362 return base::StringPrintf(
363 "'%s' is not allowed for specified context type content script, "
364 " extension page, web page, etc.).",
365 name().c_str());
366 case INVALID_LOCATION:
367 return base::StringPrintf(
368 "'%s' is not allowed for specified install location.",
369 name().c_str());
370 case INVALID_PLATFORM:
371 return base::StringPrintf(
372 "'%s' is not allowed for specified platform.",
373 name().c_str());
374 case INVALID_MIN_MANIFEST_VERSION:
375 return base::StringPrintf(
376 "'%s' requires manifest version of at least %d.",
377 name().c_str(),
378 min_manifest_version_);
379 case INVALID_MAX_MANIFEST_VERSION:
380 return base::StringPrintf(
381 "'%s' requires manifest version of %d or lower.",
382 name().c_str(),
383 max_manifest_version_);
384 case NOT_PRESENT:
385 return base::StringPrintf(
386 "'%s' requires a different Feature that is not present.",
387 name().c_str());
388 case UNSUPPORTED_CHANNEL:
389 return base::StringPrintf(
390 "'%s' requires Google Chrome %s channel or newer, and this is the "
391 "%s channel.",
392 name().c_str(),
393 GetChannelName(channel_).c_str(),
394 GetChannelName(GetCurrentChannel()).c_str());
395 }
396
397 NOTREACHED();
398 return "";
399 }
400
401 60
402 } // namespace extensions 61 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/common/extensions/features/feature.h ('k') | chrome/common/extensions/features/feature_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698