1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
|
#!/usr/bin/env python3
# Copyright 2012 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import copy
import os
import re
import sys
def IsGroupOrAtomicGroup(policy):
return policy['type'] == 'group' or policy['type'] == 'atomic_group'
class PolicyTemplateGenerator:
'''Generates template text for a particular platform.
This class is used to traverse a JSON structure from a .json template
definition metafile and merge GUI message string definitions that come
from a .grd resource tree onto it. After this, it can be used to output
this data to policy template files using TemplateWriter objects.
'''
def _ImportMessage(self, msg_txt):
lines = msg_txt.split('\n')
# Strip any extra leading spaces, but keep useful indentation:
min_leading_spaces = min(list(self._IterateLeadingSpaces(lines)) or [0])
if min_leading_spaces > 0:
lstrip_pattern = re.compile('^[ ]{1,%s}' % min_leading_spaces)
lines = [lstrip_pattern.sub('', line) for line in lines]
# Strip all trailing spaces:
lines = [line.rstrip() for line in lines]
return "\n".join(lines)
def _IterateLeadingSpaces(self, lines):
'''Yields the number of leading spaces on each line, skipping lines which
have no leading spaces.'''
for line in lines:
match = re.search('^[ ]+', line)
if match:
yield len(match.group(0))
def __init__(self, config, policy_data):
'''Initializes this object with all the data necessary to output a
policy template.
Args:
config: Writer configuration.
policy_data: The list of defined policies and groups, as parsed from the
policy metafile. See
components/policy/resources/policy_templates.json
for description and content.
'''
# List of all the policies. Create a copy since the data is modified.
self._policy_data = copy.deepcopy(policy_data)
# Localized messages to be inserted to the policy_definitions structure:
self._messages = self._policy_data['messages']
self._config = config
for key in self._messages.keys():
self._messages[key]['text'] = self._ImportMessage(
self._messages[key]['text'])
self._AddGroups(self._policy_data['policy_definitions'])
self._AddAtomicGroups(self._policy_data['policy_definitions'],
self._policy_data['policy_atomic_group_definitions'])
self._policy_data[
'policy_atomic_group_definitions'] = self._ExpandAtomicGroups(
self._policy_data['policy_definitions'],
self._policy_data['policy_atomic_group_definitions'])
self._ProcessPolicyList(
self._policy_data['policy_atomic_group_definitions'])
self._policy_data['policy_definitions'] = self._ExpandGroups(
self._policy_data['policy_definitions'])
self._policy_definitions = self._policy_data['policy_definitions']
self._ProcessPolicyList(self._policy_definitions)
def _ProcessProductPlatformString(self, product_platform_string):
'''Splits the |product_platform_string| string to product and a list of
platforms.'''
if '.' in product_platform_string:
product, platform = product_platform_string.split('.')
if platform == '*':
# e.g.: 'chrome.*:8-10'
platforms = ['linux', 'mac', 'win']
else:
# e.g.: 'chrome.win:-10'
platforms = [platform]
else:
# e.g.: 'chrome_frame:7-'
product, platform = {
'android': ('chrome', 'android'),
'webview_android': ('webview', 'android'),
'ios': ('chrome', 'ios'),
'chrome_os': ('chrome_os', 'chrome_os'),
'chrome_frame': ('chrome_frame', 'win'),
'fuchsia': ('chrome', 'fuchsia'),
}[product_platform_string]
platforms = [platform]
return product, platforms
def _ProcessSupportedOn(self, supported_on):
'''Parses and converts the string items of the list of supported platforms
into dictionaries.
Args:
supported_on: The list of supported platforms. E.g.:
['chrome.win:8-10', 'chrome_frame:10-']
Returns:
supported_on: The list with its items converted to dictionaries. E.g.:
[{
'product': 'chrome',
'platform': 'win',
'since_version': '8',
'until_version': '10'
}, {
'product': 'chrome_frame',
'platform': 'win',
'since_version': '10',
'until_version': ''
}]
'''
result = []
for supported_on_item in supported_on:
product_platform_part, version_part = supported_on_item.split(':')
product, platforms = self._ProcessProductPlatformString(
product_platform_part)
since_version, until_version = version_part.split('-')
for platform in platforms:
result.append({
'product': product,
'platform': platform,
'since_version': since_version,
'until_version': until_version
})
return result
def _ProcessFutureOn(self, future_on):
'''Parses and converts the |future_on| strings into a list of dictionaries
contain product and platform string pair.
Args:
future_on: A list of platform strings. E.g.:
['chrome.win', 'chromeos']
Returns:
future_on: A list of dictionaries. E.g.:
[{
'product': 'chrome',
'platform': 'win',
},{
'product': 'chrome_os',
'platform': 'chrome_os',
}]
'''
result = []
for future in future_on:
product, platforms = self._ProcessProductPlatformString(future)
for platform in platforms:
result.append({
'product': product,
'platform': platform,
})
return result
def _ProcessPolicy(self, policy):
'''Processes localized message strings in a policy or a group.
Also breaks up the content of 'supported_on' attribute into a list.
Args:
policy: The data structure of the policy or group, that will get message
strings here.
'''
if policy['type'] != 'atomic_group':
policy['desc'] = self._ImportMessage(policy['desc'])
policy['caption'] = self._ImportMessage(policy['caption'])
if 'label' in policy:
policy['label'] = self._ImportMessage(policy['label'])
if 'arc_support' in policy:
policy['arc_support'] = self._ImportMessage(policy['arc_support'])
if IsGroupOrAtomicGroup(policy):
self._ProcessPolicyList(policy['policies'])
elif policy['type'] in ('string-enum', 'int-enum', 'string-enum-list'):
# Iterate through all the items of an enum-type policy, and add captions.
for item in policy['items']:
item['caption'] = self._ImportMessage(item['caption'])
if 'supported_on' in item:
item['supported_on'] = self._ProcessSupportedOn(item['supported_on'])
if not IsGroupOrAtomicGroup(policy):
if not 'label' in policy:
# If 'label' is not specified, then it defaults to 'caption':
policy['label'] = policy['caption']
policy['supported_on'] = self._ProcessSupportedOn(
policy.get('supported_on', []))
policy['future_on'] = self._ProcessFutureOn(policy.get('future_on', []))
def _ProcessPolicyList(self, policy_list):
'''Adds localized message strings to each item in a list of policies and
groups. Also breaks up the content of 'supported_on' attributes into lists
of dictionaries.
Args:
policy_list: A list of policies and groups. Message strings will be added
for each item and to their child items, recursively.
'''
for policy in policy_list:
self._ProcessPolicy(policy)
def GetTemplateText(self, template_writer):
'''Generates the text of the template from the arguments given
to the constructor, using a given TemplateWriter.
Args:
template_writer: An object implementing TemplateWriter. Its methods
are called here for each item of self._policy_data.
Returns:
The text of the generated template.
'''
# Create a copy, so that writers can't screw up subsequent writers.
policy_data_copy = copy.deepcopy(self._policy_data)
return template_writer.WriteTemplate(policy_data_copy)
def _AddGroups(self, policy_list):
'''Adds a 'group' field, which is set to be the group's name, to the
policies that are part of a group.
Args:
policy_list: A list of policies and groups whose policies will have a
'group' field added.
'''
groups = [policy for policy in policy_list if policy['type'] == 'group']
policy_lookup = {
policy['name']: policy
for policy in policy_list
if not IsGroupOrAtomicGroup(policy)
}
for group in groups:
for policy_name in group['policies']:
policy_lookup[policy_name]['group'] = group['name']
def _AddAtomicGroups(self, policy_list, policy_atomic_groups):
'''Adds an 'atomic_group' field to the policies that are part of an atomic
group.
Args:
policy_list: A list of policies and groups.
policy_atomic_groups: A list of policy atomic groups
'''
policy_lookup = {
policy['name']: policy
for policy in policy_list
if not IsGroupOrAtomicGroup(policy)
}
for group in policy_atomic_groups:
for policy_name in group['policies']:
policy_lookup[policy_name]['atomic_group'] = group['name']
break
def _ExpandAtomicGroups(self, policy_list, policy_atomic_groups):
'''Replaces policies names inside atomic group definitions for actual
policies definitions.
Args:
policy_list: A list of policies and groups.
Returns:
Modified policy_list
'''
policies = [
policy for policy in policy_list if not IsGroupOrAtomicGroup(policy)
]
for group in policy_atomic_groups:
group['type'] = 'atomic_group'
expanded = self._ExpandGroups(policies + policy_atomic_groups)
expanded = [policy for policy in expanded if IsGroupOrAtomicGroup(policy)]
return copy.deepcopy(expanded)
def _ExpandGroups(self, policy_list):
'''Replaces policies names inside group definitions for actual policies
definitions. If policy does not belong to any group, leave it as is.
Args:
policy_list: A list of policies and groups.
Returns:
Modified policy_list
'''
groups = [policy for policy in policy_list if IsGroupOrAtomicGroup(policy)]
policies = {
policy['name']: policy
for policy in policy_list
if not IsGroupOrAtomicGroup(policy)
}
policies_in_groups = set()
result_policies = []
for group in groups:
group_policies = group['policies']
expanded_policies = [
policies[policy_name] for policy_name in group_policies
]
assert policies_in_groups.isdisjoint(group_policies)
policies_in_groups.update(group_policies)
group['policies'] = expanded_policies
result_policies.append(group)
result_policies.extend([
policy for policy in policy_list if not IsGroupOrAtomicGroup(policy) and
policy['name'] not in policies_in_groups
])
return result_policies
|