OLD | NEW |
| (Empty) |
1 # Copyright 2013 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. | |
4 | |
5 ''' | |
6 Utility functions for working with the Feature abstraction. Features are grouped | |
7 into a dictionary by name. Each Feature is guaranteed to have the following two | |
8 keys: | |
9 name - a string, the name of the feature | |
10 platform - a list containing 'apps' or 'extensions', both, or neither. | |
11 | |
12 A Feature may have other keys from a _features.json file as well. Features with | |
13 a whitelist are ignored as they are only useful to specific apps or extensions. | |
14 ''' | |
15 | |
16 from copy import copy | |
17 | |
18 from branch_utility import BranchUtility | |
19 | |
20 | |
21 def _GetPlatformsForExtensionTypes(extension_types): | |
22 platforms = [] | |
23 if extension_types == 'all' or 'platform_app' in extension_types: | |
24 platforms.append('apps') | |
25 if extension_types == 'all' or 'extension' in extension_types: | |
26 platforms.append('extensions') | |
27 return platforms | |
28 | |
29 | |
30 def Parse(features_json): | |
31 '''Process JSON from a _features.json file, standardizing it into a dictionary | |
32 of Features. | |
33 ''' | |
34 features = {} | |
35 | |
36 def ignore_feature(name, value): | |
37 '''Returns true if this feature should be ignored. Features are ignored if | |
38 they are only available to whitelisted apps or component extensions/apps, as | |
39 in these cases the APIs are not available to public developers. | |
40 | |
41 Private APIs are also unavailable to public developers, but logic elsewhere | |
42 makes sure they are not listed. So they shouldn't be ignored via this | |
43 mechanism. | |
44 ''' | |
45 if name.endswith('Private'): | |
46 return False | |
47 return value.get('location') == 'component' or 'whitelist' in value | |
48 | |
49 for name, rawvalue in features_json.iteritems(): | |
50 # Some feature names correspond to a list, typically because they're | |
51 # whitelisted in stable for certain extensions and available in dev for | |
52 # everybody else. Force a list down to a single feature by attempting to | |
53 # remove the entries that don't affect the typical usage of an API. | |
54 if isinstance(rawvalue, list): | |
55 available_values = [subvalue for subvalue in rawvalue | |
56 if not ignore_feature(name, subvalue)] | |
57 if not available_values: | |
58 continue | |
59 | |
60 if len(available_values) == 1: | |
61 value = available_values[0] | |
62 else: | |
63 # Multiple available values probably implies different feature | |
64 # configurations for apps vs extensions. Currently, this is 'commands'. | |
65 # To get the ball rolling, add a hack to combine the extension types. | |
66 # See http://crbug.com/316194. | |
67 extension_types = set() | |
68 for available_value in available_values: | |
69 extension_types.update(available_value.get('extension_types', ())) | |
70 | |
71 # For the base value, select the one with the most recent availability. | |
72 channel_names = BranchUtility.GetAllChannelNames() | |
73 available_values.sort( | |
74 key=lambda v: channel_names.index(v.get('channel', 'stable'))) | |
75 | |
76 # Note: copy() is enough because only a single toplevel key is modified. | |
77 value = copy(available_values[0]) | |
78 value['extension_types'] = list(extension_types) | |
79 else: | |
80 value = rawvalue | |
81 | |
82 if ignore_feature(name, value): | |
83 continue | |
84 | |
85 # Now we transform 'extension_types' into the more useful 'platforms'. | |
86 # | |
87 # But first, note that 'platforms' has a double meaning. In the docserver | |
88 # model (what we're in the process of generating) it means 'apps' vs | |
89 # 'extensions'. In the JSON features as read from Chrome it means 'win' vs | |
90 # 'mac'. Ignore the latter. | |
91 value.pop('platforms', None) | |
92 extension_types = value.pop('extension_types', None) | |
93 | |
94 platforms = [] | |
95 if extension_types is not None: | |
96 platforms = _GetPlatformsForExtensionTypes(extension_types) | |
97 | |
98 features[name] = { | |
99 'name': name, | |
100 'platforms': platforms, | |
101 } | |
102 features[name].update(value) | |
103 | |
104 return features | |
105 | |
106 | |
107 def Filtered(features, platform): | |
108 '''Create a new Features dictionary from |features| that contains only items | |
109 relevant to |platform|. Feature values are unmodified; callers should | |
110 deepcopy() if they need to modify them. | |
111 ''' | |
112 return dict((name, feature) for name, feature in features.iteritems() | |
113 if platform in feature['platforms']) | |
114 | |
115 | |
116 def MergedWith(features, other): | |
117 '''Merge |features| with an additional dictionary to create a new features | |
118 dictionary. If a feature is common to both |features| and |other|, then it is | |
119 merged using the standard dictionary update instead of being overwritten. | |
120 Returns the new Features dictionary. | |
121 ''' | |
122 for key, value in other.iteritems(): | |
123 if key in features: | |
124 features[key].update(value) | |
125 else: | |
126 features[key] = value | |
127 | |
128 # Ensure the Feature schema is enforced for all added items. | |
129 if not 'name' in features[key]: | |
130 features[key]['name'] = key | |
131 if not 'platforms' in features[key]: | |
132 features[key]['platforms'] = [] | |
133 | |
134 return features | |
OLD | NEW |