| 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 |