Make sure webrtc::VideoSource is released when WebRtcVideoTrackAdapter is destroyed.
[chromium-blink-merge.git] / components / policy / tools / generate_policy_source.py
blob0be012abf272e7c6998a84d851e007416443d5ec
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 '''python %prog [options] platform chromium_os_flag template
8 platform specifies which platform source is being generated for
9 and can be one of (win, mac, linux)
10 chromium_os_flag should be 1 if this is a Chromium OS build
11 template is the path to a .json policy template file.'''
13 from __future__ import with_statement
14 from functools import partial
15 import json
16 from optparse import OptionParser
17 import re
18 import sys
19 import textwrap
20 import types
23 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome'
24 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium'
27 class PolicyDetails:
28 """Parses a policy template and caches all its details."""
30 # Maps policy types to a tuple with 3 other types:
31 # - the equivalent base::Value::Type or 'TYPE_EXTERNAL' if the policy
32 # references external data
33 # - the equivalent Protobuf field type
34 # - the name of one of the protobufs for shared policy types
35 # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type
36 # that can also be used to represent lists of other JSON objects.
37 TYPE_MAP = {
38 'dict': ('TYPE_DICTIONARY', 'string', 'String'),
39 'external': ('TYPE_EXTERNAL', 'string', 'String'),
40 'int': ('TYPE_INTEGER', 'int64', 'Integer'),
41 'int-enum': ('TYPE_INTEGER', 'int64', 'Integer'),
42 'list': ('TYPE_LIST', 'StringList', 'StringList'),
43 'main': ('TYPE_BOOLEAN', 'bool', 'Boolean'),
44 'string': ('TYPE_STRING', 'string', 'String'),
45 'string-enum': ('TYPE_STRING', 'string', 'String'),
48 class EnumItem:
49 def __init__(self, item):
50 self.caption = PolicyDetails._RemovePlaceholders(item['caption'])
51 self.value = item['value']
53 def __init__(self, policy, os, is_chromium_os):
54 self.id = policy['id']
55 self.name = policy['name']
56 self.is_deprecated = policy.get('deprecated', False)
57 self.is_device_only = policy.get('device_only', False)
58 self.schema = policy.get('schema', {})
60 expected_platform = 'chrome_os' if is_chromium_os else os.lower()
61 self.platforms = []
62 for platform, version in [ p.split(':') for p in policy['supported_on'] ]:
63 if not version.endswith('-'):
64 continue
66 if platform.startswith('chrome.'):
67 platform_sub = platform[7:]
68 if platform_sub == '*':
69 self.platforms.extend(['win', 'mac', 'linux'])
70 else:
71 self.platforms.append(platform_sub)
72 else:
73 self.platforms.append(platform)
75 self.platforms.sort()
76 self.is_supported = expected_platform in self.platforms
78 if not PolicyDetails.TYPE_MAP.has_key(policy['type']):
79 raise NotImplementedError('Unknown policy type for %s: %s' %
80 (policy['name'], policy['type']))
81 self.policy_type, self.protobuf_type, self.policy_protobuf_type = \
82 PolicyDetails.TYPE_MAP[policy['type']]
83 self.schema = policy['schema']
85 self.desc = '\n'.join(
86 map(str.strip,
87 PolicyDetails._RemovePlaceholders(policy['desc']).splitlines()))
88 self.caption = PolicyDetails._RemovePlaceholders(policy['caption'])
89 self.max_size = policy.get('max_size', 0)
91 items = policy.get('items')
92 if items is None:
93 self.items = None
94 else:
95 self.items = [ PolicyDetails.EnumItem(entry) for entry in items ]
97 PH_PATTERN = re.compile('<ph[^>]*>([^<]*|[^<]*<ex>([^<]*)</ex>[^<]*)</ph>')
99 # Simplistic grit placeholder stripper.
100 @staticmethod
101 def _RemovePlaceholders(text):
102 result = ''
103 pos = 0
104 for m in PolicyDetails.PH_PATTERN.finditer(text):
105 result += text[pos:m.start(0)]
106 result += m.group(2) or m.group(1)
107 pos = m.end(0)
108 result += text[pos:]
109 return result
112 def main():
113 parser = OptionParser(usage=__doc__)
114 parser.add_option('--pch', '--policy-constants-header', dest='header_path',
115 help='generate header file of policy constants',
116 metavar='FILE')
117 parser.add_option('--pcc', '--policy-constants-source', dest='source_path',
118 help='generate source file of policy constants',
119 metavar='FILE')
120 parser.add_option('--cpp', '--cloud-policy-protobuf',
121 dest='cloud_policy_proto_path',
122 help='generate cloud policy protobuf file',
123 metavar='FILE')
124 parser.add_option('--csp', '--chrome-settings-protobuf',
125 dest='chrome_settings_proto_path',
126 help='generate chrome settings protobuf file',
127 metavar='FILE')
128 parser.add_option('--cpd', '--cloud-policy-decoder',
129 dest='cloud_policy_decoder_path',
130 help='generate C++ code decoding the cloud policy protobuf',
131 metavar='FILE')
133 (opts, args) = parser.parse_args()
135 if len(args) != 3:
136 print 'exactly platform, chromium_os flag and input file must be specified.'
137 parser.print_help()
138 return 2
140 os = args[0]
141 is_chromium_os = args[1] == '1'
142 template_file_name = args[2]
144 template_file_contents = _LoadJSONFile(template_file_name)
145 policy_details = [ PolicyDetails(policy, os, is_chromium_os)
146 for policy in _Flatten(template_file_contents) ]
147 sorted_policy_details = sorted(policy_details, key=lambda policy: policy.name)
149 def GenerateFile(path, writer, sorted=False):
150 if path:
151 with open(path, 'w') as f:
152 _OutputGeneratedWarningHeader(f, template_file_name)
153 writer(sorted and sorted_policy_details or policy_details, os, f)
155 GenerateFile(opts.header_path, _WritePolicyConstantHeader, sorted=True)
156 GenerateFile(opts.source_path, _WritePolicyConstantSource, sorted=True)
157 GenerateFile(opts.cloud_policy_proto_path, _WriteCloudPolicyProtobuf)
158 GenerateFile(opts.chrome_settings_proto_path, _WriteChromeSettingsProtobuf)
159 GenerateFile(opts.cloud_policy_decoder_path, _WriteCloudPolicyDecoder)
161 return 0
164 #------------------ shared helpers ---------------------------------#
166 def _OutputGeneratedWarningHeader(f, template_file_path):
167 f.write('//\n'
168 '// DO NOT MODIFY THIS FILE DIRECTLY!\n'
169 '// IT IS GENERATED BY generate_policy_source.py\n'
170 '// FROM ' + template_file_path + '\n'
171 '//\n\n')
174 COMMENT_WRAPPER = textwrap.TextWrapper()
175 COMMENT_WRAPPER.width = 80
176 COMMENT_WRAPPER.initial_indent = '// '
177 COMMENT_WRAPPER.subsequent_indent = '// '
178 COMMENT_WRAPPER.replace_whitespace = False
181 # Writes a comment, each line prefixed by // and wrapped to 80 spaces.
182 def _OutputComment(f, comment):
183 for line in comment.splitlines():
184 if len(line) == 0:
185 f.write('//')
186 else:
187 f.write(COMMENT_WRAPPER.fill(line))
188 f.write('\n')
191 # Returns an iterator over all the policies in |template_file_contents|.
192 def _Flatten(template_file_contents):
193 for policy in template_file_contents['policy_definitions']:
194 if policy['type'] == 'group':
195 for sub_policy in policy['policies']:
196 yield sub_policy
197 else:
198 yield policy
201 def _LoadJSONFile(json_file):
202 with open(json_file, 'r') as f:
203 text = f.read()
204 return eval(text)
207 #------------------ policy constants header ------------------------#
209 def _WritePolicyConstantHeader(policies, os, f):
210 f.write('#ifndef CHROME_COMMON_POLICY_CONSTANTS_H_\n'
211 '#define CHROME_COMMON_POLICY_CONSTANTS_H_\n'
212 '\n'
213 '#include <string>\n'
214 '\n'
215 '#include "base/basictypes.h"\n'
216 '#include "base/values.h"\n'
217 '#include "components/policy/core/common/policy_details.h"\n'
218 '\n'
219 'namespace policy {\n'
220 '\n'
221 'namespace internal {\n'
222 'struct SchemaData;\n'
223 '}\n\n')
225 if os == 'win':
226 f.write('// The windows registry path where Chrome policy '
227 'configuration resides.\n'
228 'extern const wchar_t kRegistryChromePolicyKey[];\n')
230 f.write('// Returns the PolicyDetails for |policy| if |policy| is a known\n'
231 '// Chrome policy, otherwise returns NULL.\n'
232 'const PolicyDetails* GetChromePolicyDetails('
233 'const std::string& policy);\n'
234 '\n'
235 '// Returns the schema data of the Chrome policy schema.\n'
236 'const internal::SchemaData* GetChromeSchemaData();\n'
237 '\n')
238 f.write('// Key names for the policy settings.\n'
239 'namespace key {\n\n')
240 for policy in policies:
241 # TODO(joaodasilva): Include only supported policies in
242 # configuration_policy_handler.cc and configuration_policy_handler_list.cc
243 # so that these names can be conditional on 'policy.is_supported'.
244 # http://crbug.com/223616
245 f.write('extern const char k' + policy.name + '[];\n')
246 f.write('\n} // namespace key\n\n'
247 '} // namespace policy\n\n'
248 '#endif // CHROME_COMMON_POLICY_CONSTANTS_H_\n')
251 #------------------ policy constants source ------------------------#
253 # A mapping of the simple schema types to base::Value::Types.
254 SIMPLE_SCHEMA_NAME_MAP = {
255 'boolean': 'TYPE_BOOLEAN',
256 'integer': 'TYPE_INTEGER',
257 'null' : 'TYPE_NULL',
258 'number' : 'TYPE_DOUBLE',
259 'string' : 'TYPE_STRING',
262 class SchemaNodesGenerator:
263 """Builds the internal structs to represent a JSON schema."""
265 def __init__(self, shared_strings):
266 """Creates a new generator.
268 |shared_strings| is a map of strings to a C expression that evaluates to
269 that string at runtime. This mapping can be used to reuse existing string
270 constants."""
271 self.shared_strings = shared_strings
272 self.schema_nodes = []
273 self.property_nodes = []
274 self.properties_nodes = []
275 self.restriction_nodes = []
276 self.int_enums = []
277 self.string_enums = []
278 self.simple_types = {
279 'boolean': None,
280 'integer': None,
281 'null': None,
282 'number': None,
283 'string': None,
285 self.stringlist_type = None
286 self.ranges = {}
287 self.id_map = {}
289 def GetString(self, s):
290 if s in self.shared_strings:
291 return self.shared_strings[s]
292 # Generate JSON escaped string, which is slightly different from desired
293 # C/C++ escaped string. Known differences includes unicode escaping format.
294 return json.dumps(s)
296 def AppendSchema(self, type, extra, comment=''):
297 index = len(self.schema_nodes)
298 self.schema_nodes.append((type, extra, comment))
299 return index
301 def AppendRestriction(self, first, second):
302 r = (str(first), str(second))
303 if not r in self.ranges:
304 self.ranges[r] = len(self.restriction_nodes)
305 self.restriction_nodes.append(r)
306 return self.ranges[r]
308 def GetSimpleType(self, name):
309 if self.simple_types[name] == None:
310 self.simple_types[name] = self.AppendSchema(
311 SIMPLE_SCHEMA_NAME_MAP[name],
313 'simple type: ' + name)
314 return self.simple_types[name]
316 def GetStringList(self):
317 if self.stringlist_type == None:
318 self.stringlist_type = self.AppendSchema(
319 'TYPE_LIST',
320 self.GetSimpleType('string'),
321 'simple type: stringlist')
322 return self.stringlist_type
324 def SchemaHaveRestriction(self, schema):
325 return any(keyword in schema for keyword in
326 ['minimum', 'maximum', 'enum', 'pattern'])
328 def IsConsecutiveInterval(self, seq):
329 sortedSeq = sorted(seq)
330 return all(sortedSeq[i] + 1 == sortedSeq[i + 1]
331 for i in xrange(len(sortedSeq) - 1))
333 def GetEnumIntegerType(self, schema, name):
334 assert all(type(x) == int for x in schema['enum'])
335 possible_values = schema['enum']
336 if self.IsConsecutiveInterval(possible_values):
337 index = self.AppendRestriction(max(possible_values), min(possible_values))
338 return self.AppendSchema('TYPE_INTEGER', index,
339 'integer with enumeration restriction (use range instead): %s' % name)
340 offset_begin = len(self.int_enums)
341 self.int_enums += possible_values
342 offset_end = len(self.int_enums)
343 return self.AppendSchema('TYPE_INTEGER',
344 self.AppendRestriction(offset_begin, offset_end),
345 'integer with enumeration restriction: %s' % name)
347 def GetEnumStringType(self, schema, name):
348 assert all(type(x) == str for x in schema['enum'])
349 offset_begin = len(self.string_enums)
350 self.string_enums += schema['enum']
351 offset_end = len(self.string_enums)
352 return self.AppendSchema('TYPE_STRING',
353 self.AppendRestriction(offset_begin, offset_end),
354 'string with enumeration restriction: %s' % name)
356 def GetEnumType(self, schema, name):
357 if len(schema['enum']) == 0:
358 raise RuntimeError('Empty enumeration in %s' % name)
359 elif schema['type'] == 'integer':
360 return self.GetEnumIntegerType(schema, name)
361 elif schema['type'] == 'string':
362 return self.GetEnumStringType(schema, name)
363 else:
364 raise RuntimeError('Unknown enumeration type in %s' % name)
366 def GetPatternType(self, schema, name):
367 if schema['type'] != 'string':
368 raise RuntimeError('Unknown pattern type in %s' % name)
369 pattern = schema['pattern']
370 # Try to compile the pattern to validate it, note that the syntax used
371 # here might be slightly different from re2.
372 # TODO(binjin): Add a python wrapper of re2 and use it here.
373 re.compile(pattern)
374 index = len(self.string_enums);
375 self.string_enums.append(pattern);
376 return self.AppendSchema('TYPE_STRING',
377 self.AppendRestriction(index, index),
378 'string with pattern restriction: %s' % name);
380 def GetRangedType(self, schema, name):
381 if schema['type'] != 'integer':
382 raise RuntimeError('Unknown ranged type in %s' % name)
383 min_value_set, max_value_set = False, False
384 if 'minimum' in schema:
385 min_value = int(schema['minimum'])
386 min_value_set = True
387 if 'maximum' in schema:
388 max_value = int(schema['minimum'])
389 max_value_set = True
390 if min_value_set and max_value_set and min_value > max_value:
391 raise RuntimeError('Invalid ranged type in %s' % name)
392 index = self.AppendRestriction(
393 str(max_value) if max_value_set else 'INT_MAX',
394 str(min_value) if min_value_set else 'INT_MIN')
395 return self.AppendSchema('TYPE_INTEGER',
396 index,
397 'integer with ranged restriction: %s' % name)
399 def Generate(self, schema, name):
400 """Generates the structs for the given schema.
402 |schema|: a valid JSON schema in a dictionary.
403 |name|: the name of the current node, for the generated comments."""
404 if schema.has_key('$ref'):
405 if schema.has_key('id'):
406 raise RuntimeError("Schemas with a $ref can't have an id")
407 if not isinstance(schema['$ref'], types.StringTypes):
408 raise RuntimeError("$ref attribute must be a string")
409 return schema['$ref']
410 if schema['type'] in self.simple_types:
411 if not self.SchemaHaveRestriction(schema):
412 # Simple types use shared nodes.
413 return self.GetSimpleType(schema['type'])
414 elif 'enum' in schema:
415 return self.GetEnumType(schema, name)
416 elif 'pattern' in schema:
417 return self.GetPatternType(schema, name)
418 else:
419 return self.GetRangedType(schema, name)
421 if schema['type'] == 'array':
422 # Special case for lists of strings, which is a common policy type.
423 if schema['items']['type'] == 'string':
424 return self.GetStringList()
425 return self.AppendSchema('TYPE_LIST',
426 self.GenerateAndCollectID(schema['items'], 'items of ' + name))
427 elif schema['type'] == 'object':
428 # Reserve an index first, so that dictionaries come before their
429 # properties. This makes sure that the root node is the first in the
430 # SchemaNodes array.
431 index = self.AppendSchema('TYPE_DICTIONARY', -1)
433 if 'additionalProperties' in schema:
434 additionalProperties = self.GenerateAndCollectID(
435 schema['additionalProperties'],
436 'additionalProperties of ' + name)
437 else:
438 additionalProperties = -1
440 # Properties must be sorted by name, for the binary search lookup.
441 # Note that |properties| must be evaluated immediately, so that all the
442 # recursive calls to Generate() append the necessary child nodes; if
443 # |properties| were a generator then this wouldn't work.
444 sorted_properties = sorted(schema.get('properties', {}).items())
445 properties = [
446 (self.GetString(key), self.GenerateAndCollectID(subschema, key))
447 for key, subschema in sorted_properties ]
449 pattern_properties = []
450 for pattern, subschema in schema.get('patternProperties', {}).items():
451 pattern_properties.append((self.GetString(pattern),
452 self.GenerateAndCollectID(subschema, pattern)));
454 begin = len(self.property_nodes)
455 self.property_nodes += properties
456 end = len(self.property_nodes)
457 self.property_nodes += pattern_properties
458 pattern_end = len(self.property_nodes)
460 if index == 0:
461 self.root_properties_begin = begin
462 self.root_properties_end = end
464 extra = len(self.properties_nodes)
465 self.properties_nodes.append((begin, end, pattern_end,
466 additionalProperties, name))
468 # Set the right data at |index| now.
469 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name)
470 return index
471 else:
472 assert False
474 def GenerateAndCollectID(self, schema, name):
475 """A wrapper of Generate(), will take the return value, check and add 'id'
476 attribute to self.id_map. The wrapper needs to be used for every call to
477 Generate().
479 index = self.Generate(schema, name)
480 if not schema.has_key('id'):
481 return index
482 id_str = schema['id']
483 if self.id_map.has_key(id_str):
484 raise RuntimeError('Duplicated id: ' + id_str)
485 self.id_map[id_str] = index
486 return index
488 def Write(self, f):
489 """Writes the generated structs to the given file.
491 |f| an open file to write to."""
492 f.write('const internal::SchemaNode kSchemas[] = {\n'
493 '// Type Extra\n')
494 for type, extra, comment in self.schema_nodes:
495 type += ','
496 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment))
497 f.write('};\n\n')
499 if self.property_nodes:
500 f.write('const internal::PropertyNode kPropertyNodes[] = {\n'
501 '// Property #Schema\n')
502 for key, schema in self.property_nodes:
503 key += ','
504 f.write(' { %-50s %6d },\n' % (key, schema))
505 f.write('};\n\n')
507 if self.properties_nodes:
508 f.write('const internal::PropertiesNode kProperties[] = {\n'
509 '// Begin End PatternEnd Additional Properties\n')
510 for node in self.properties_nodes:
511 f.write(' { %5d, %5d, %10d, %5d }, // %s\n' % node)
512 f.write('};\n\n')
514 if self.restriction_nodes:
515 f.write('const internal::RestrictionNode kRestrictionNodes[] = {\n')
516 f.write('// FIRST, SECOND\n')
517 for first, second in self.restriction_nodes:
518 f.write(' {{ %-8s %4s}},\n' % (first + ',', second))
519 f.write('};\n\n')
521 if self.int_enums:
522 f.write('const int kIntegerEnumerations[] = {\n')
523 for possible_values in self.int_enums:
524 f.write(' %d,\n' % possible_values)
525 f.write('};\n\n')
527 if self.string_enums:
528 f.write('const char* kStringEnumerations[] = {\n')
529 for possible_values in self.string_enums:
530 f.write(' %s,\n' % self.GetString(possible_values))
531 f.write('};\n\n')
533 f.write('const internal::SchemaData kChromeSchemaData = {\n'
534 ' kSchemas,\n')
535 f.write(' kPropertyNodes,\n' if self.property_nodes else ' NULL,\n')
536 f.write(' kProperties,\n' if self.properties_nodes else ' NULL,\n')
537 f.write(' kRestrictionNodes,\n' if self.restriction_nodes else ' NULL,\n')
538 f.write(' kIntegerEnumerations,\n' if self.int_enums else ' NULL,\n')
539 f.write(' kStringEnumerations,\n' if self.string_enums else ' NULL,\n')
540 f.write('};\n\n')
542 def GetByID(self, id_str):
543 if not isinstance(id_str, types.StringTypes):
544 return id_str
545 if not self.id_map.has_key(id_str):
546 raise RuntimeError('Invalid $ref: ' + id_str)
547 return self.id_map[id_str]
549 def ResolveID(self, index, params):
550 return params[:index] + (self.GetByID(params[index]),) + params[index+1:]
552 def ResolveReferences(self):
553 """Resolve reference mapping, required to be called after Generate()
555 After calling Generate(), the type of indices used in schema structures
556 might be either int or string. An int type suggests that it's a resolved
557 index, but for string type it's unresolved. Resolving a reference is as
558 simple as looking up for corresponding ID in self.id_map, and replace the
559 old index with the mapped index.
561 self.schema_nodes = map(partial(self.ResolveID, 1), self.schema_nodes)
562 self.property_nodes = map(partial(self.ResolveID, 1), self.property_nodes)
563 self.properties_nodes = map(partial(self.ResolveID, 3),
564 self.properties_nodes)
566 def _WritePolicyConstantSource(policies, os, f):
567 f.write('#include "policy/policy_constants.h"\n'
568 '\n'
569 '#include <algorithm>\n'
570 '#include <climits>\n'
571 '\n'
572 '#include "base/logging.h"\n'
573 '#include "components/policy/core/common/schema_internal.h"\n'
574 '\n'
575 'namespace policy {\n'
576 '\n'
577 'namespace {\n'
578 '\n')
580 # Generate the Chrome schema.
581 chrome_schema = {
582 'type': 'object',
583 'properties': {},
585 shared_strings = {}
586 for policy in policies:
587 shared_strings[policy.name] = "key::k%s" % policy.name
588 if policy.is_supported:
589 chrome_schema['properties'][policy.name] = policy.schema
591 # Note: this list must be kept in sync with the known property list of the
592 # Chrome schema, so that binary seaching in the PropertyNode array gets the
593 # right index on this array as well. See the implementation of
594 # GetChromePolicyDetails() below.
595 f.write('const PolicyDetails kChromePolicyDetails[] = {\n'
596 '// is_deprecated is_device_policy id max_external_data_size\n')
597 for policy in policies:
598 if policy.is_supported:
599 f.write(' { %-14s %-16s %3s, %24s },\n' % (
600 'true,' if policy.is_deprecated else 'false,',
601 'true,' if policy.is_device_only else 'false,',
602 policy.id,
603 policy.max_size))
604 f.write('};\n\n')
606 schema_generator = SchemaNodesGenerator(shared_strings)
607 schema_generator.GenerateAndCollectID(chrome_schema, 'root node')
608 schema_generator.ResolveReferences()
609 schema_generator.Write(f)
611 f.write('bool CompareKeys(const internal::PropertyNode& node,\n'
612 ' const std::string& key) {\n'
613 ' return node.key < key;\n'
614 '}\n\n')
616 f.write('} // namespace\n\n')
618 if os == 'win':
619 f.write('#if defined(GOOGLE_CHROME_BUILD)\n'
620 'const wchar_t kRegistryChromePolicyKey[] = '
621 'L"' + CHROME_POLICY_KEY + '";\n'
622 '#else\n'
623 'const wchar_t kRegistryChromePolicyKey[] = '
624 'L"' + CHROMIUM_POLICY_KEY + '";\n'
625 '#endif\n\n')
627 f.write('const internal::SchemaData* GetChromeSchemaData() {\n'
628 ' return &kChromeSchemaData;\n'
629 '}\n\n')
631 f.write('const PolicyDetails* GetChromePolicyDetails('
632 'const std::string& policy) {\n'
633 ' // First index in kPropertyNodes of the Chrome policies.\n'
634 ' static const int begin_index = %s;\n'
635 ' // One-past-the-end of the Chrome policies in kPropertyNodes.\n'
636 ' static const int end_index = %s;\n' %
637 (schema_generator.root_properties_begin,
638 schema_generator.root_properties_end))
639 f.write(' const internal::PropertyNode* begin =\n'
640 ' kPropertyNodes + begin_index;\n'
641 ' const internal::PropertyNode* end = kPropertyNodes + end_index;\n'
642 ' const internal::PropertyNode* it =\n'
643 ' std::lower_bound(begin, end, policy, CompareKeys);\n'
644 ' if (it == end || it->key != policy)\n'
645 ' return NULL;\n'
646 ' // This relies on kPropertyNodes from begin_index to end_index\n'
647 ' // having exactly the same policies (and in the same order) as\n'
648 ' // kChromePolicyDetails, so that binary searching on the first\n'
649 ' // gets the same results as a binary search on the second would.\n'
650 ' // However, kPropertyNodes has the policy names and\n'
651 ' // kChromePolicyDetails doesn\'t, so we obtain the index into\n'
652 ' // the second array by searching the first to avoid duplicating\n'
653 ' // the policy name pointers.\n'
654 ' // Offsetting |it| from |begin| here obtains the index we\'re\n'
655 ' // looking for.\n'
656 ' size_t index = it - begin;\n'
657 ' CHECK_LT(index, arraysize(kChromePolicyDetails));\n'
658 ' return kChromePolicyDetails + index;\n'
659 '}\n\n')
661 f.write('namespace key {\n\n')
662 for policy in policies:
663 # TODO(joaodasilva): Include only supported policies in
664 # configuration_policy_handler.cc and configuration_policy_handler_list.cc
665 # so that these names can be conditional on 'policy.is_supported'.
666 # http://crbug.com/223616
667 f.write('const char k{name}[] = "{name}";\n'.format(name=policy.name))
668 f.write('\n} // namespace key\n\n'
669 '} // namespace policy\n')
672 #------------------ policy protobufs --------------------------------#
674 CHROME_SETTINGS_PROTO_HEAD = '''
675 syntax = "proto2";
677 option optimize_for = LITE_RUNTIME;
679 package enterprise_management;
681 // For StringList and PolicyOptions.
682 import "cloud_policy.proto";
687 CLOUD_POLICY_PROTO_HEAD = '''
688 syntax = "proto2";
690 option optimize_for = LITE_RUNTIME;
692 package enterprise_management;
694 message StringList {
695 repeated string entries = 1;
698 message PolicyOptions {
699 enum PolicyMode {
700 // The given settings are applied regardless of user choice.
701 MANDATORY = 0;
702 // The user may choose to override the given settings.
703 RECOMMENDED = 1;
704 // No policy value is present and the policy should be ignored.
705 UNSET = 2;
707 optional PolicyMode mode = 1 [default = MANDATORY];
710 message BooleanPolicyProto {
711 optional PolicyOptions policy_options = 1;
712 optional bool value = 2;
715 message IntegerPolicyProto {
716 optional PolicyOptions policy_options = 1;
717 optional int64 value = 2;
720 message StringPolicyProto {
721 optional PolicyOptions policy_options = 1;
722 optional string value = 2;
725 message StringListPolicyProto {
726 optional PolicyOptions policy_options = 1;
727 optional StringList value = 2;
733 # Field IDs [1..RESERVED_IDS] will not be used in the wrapping protobuf.
734 RESERVED_IDS = 2
737 def _WritePolicyProto(f, policy, fields):
738 _OutputComment(f, policy.caption + '\n\n' + policy.desc)
739 if policy.items is not None:
740 _OutputComment(f, '\nValid values:')
741 for item in policy.items:
742 _OutputComment(f, ' %s: %s' % (str(item.value), item.caption))
743 if policy.policy_type == 'TYPE_DICTIONARY':
744 _OutputComment(f, '\nValue schema:\n%s' %
745 json.dumps(policy.schema, sort_keys=True, indent=4,
746 separators=(',', ': ')))
747 _OutputComment(f, '\nSupported on: %s' % ', '.join(policy.platforms))
748 f.write('message %sProto {\n' % policy.name)
749 f.write(' optional PolicyOptions policy_options = 1;\n')
750 f.write(' optional %s %s = 2;\n' % (policy.protobuf_type, policy.name))
751 f.write('}\n\n')
752 fields += [ ' optional %sProto %s = %s;\n' %
753 (policy.name, policy.name, policy.id + RESERVED_IDS) ]
756 def _WriteChromeSettingsProtobuf(policies, os, f):
757 f.write(CHROME_SETTINGS_PROTO_HEAD)
759 fields = []
760 f.write('// PBs for individual settings.\n\n')
761 for policy in policies:
762 # Note: this protobuf also gets the unsupported policies, since it's an
763 # exaustive list of all the supported user policies on any platform.
764 if not policy.is_device_only:
765 _WritePolicyProto(f, policy, fields)
767 f.write('// --------------------------------------------------\n'
768 '// Big wrapper PB containing the above groups.\n\n'
769 'message ChromeSettingsProto {\n')
770 f.write(''.join(fields))
771 f.write('}\n\n')
774 def _WriteCloudPolicyProtobuf(policies, os, f):
775 f.write(CLOUD_POLICY_PROTO_HEAD)
776 f.write('message CloudPolicySettings {\n')
777 for policy in policies:
778 if policy.is_supported and not policy.is_device_only:
779 f.write(' optional %sPolicyProto %s = %s;\n' %
780 (policy.policy_protobuf_type, policy.name,
781 policy.id + RESERVED_IDS))
782 f.write('}\n\n')
785 #------------------ protobuf decoder -------------------------------#
787 CPP_HEAD = '''
788 #include <limits>
789 #include <string>
791 #include "base/basictypes.h"
792 #include "base/callback.h"
793 #include "base/json/json_reader.h"
794 #include "base/logging.h"
795 #include "base/memory/scoped_ptr.h"
796 #include "base/memory/weak_ptr.h"
797 #include "base/values.h"
798 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
799 #include "components/policy/core/common/external_data_fetcher.h"
800 #include "components/policy/core/common/policy_map.h"
801 #include "policy/policy_constants.h"
802 #include "policy/proto/cloud_policy.pb.h"
804 using google::protobuf::RepeatedPtrField;
806 namespace policy {
808 namespace em = enterprise_management;
810 base::Value* DecodeIntegerValue(google::protobuf::int64 value) {
811 if (value < std::numeric_limits<int>::min() ||
812 value > std::numeric_limits<int>::max()) {
813 LOG(WARNING) << "Integer value " << value
814 << " out of numeric limits, ignoring.";
815 return NULL;
818 return base::Value::CreateIntegerValue(static_cast<int>(value));
821 base::ListValue* DecodeStringList(const em::StringList& string_list) {
822 base::ListValue* list_value = new base::ListValue;
823 RepeatedPtrField<std::string>::const_iterator entry;
824 for (entry = string_list.entries().begin();
825 entry != string_list.entries().end(); ++entry) {
826 list_value->Append(base::Value::CreateStringValue(*entry));
828 return list_value;
831 base::Value* DecodeJson(const std::string& json) {
832 scoped_ptr<base::Value> root(
833 base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS));
835 if (!root)
836 LOG(WARNING) << "Invalid JSON string, ignoring: " << json;
838 // Accept any Value type that parsed as JSON, and leave it to the handler to
839 // convert and check the concrete type.
840 return root.release();
843 void DecodePolicy(const em::CloudPolicySettings& policy,
844 base::WeakPtr<CloudExternalDataManager> external_data_manager,
845 PolicyMap* map) {
849 CPP_FOOT = '''}
851 } // namespace policy
855 def _CreateValue(type, arg):
856 if type == 'TYPE_BOOLEAN':
857 return 'base::Value::CreateBooleanValue(%s)' % arg
858 elif type == 'TYPE_INTEGER':
859 return 'DecodeIntegerValue(%s)' % arg
860 elif type == 'TYPE_STRING':
861 return 'base::Value::CreateStringValue(%s)' % arg
862 elif type == 'TYPE_LIST':
863 return 'DecodeStringList(%s)' % arg
864 elif type == 'TYPE_DICTIONARY' or type == 'TYPE_EXTERNAL':
865 return 'DecodeJson(%s)' % arg
866 else:
867 raise NotImplementedError('Unknown type %s' % type)
870 def _CreateExternalDataFetcher(type, name):
871 if type == 'TYPE_EXTERNAL':
872 return 'new ExternalDataFetcher(external_data_manager, key::k%s)' % name
873 return 'NULL'
876 def _WritePolicyCode(f, policy):
877 membername = policy.name.lower()
878 proto_type = '%sPolicyProto' % policy.policy_protobuf_type
879 f.write(' if (policy.has_%s()) {\n' % membername)
880 f.write(' const em::%s& policy_proto = policy.%s();\n' %
881 (proto_type, membername))
882 f.write(' if (policy_proto.has_value()) {\n')
883 f.write(' PolicyLevel level = POLICY_LEVEL_MANDATORY;\n'
884 ' bool do_set = true;\n'
885 ' if (policy_proto.has_policy_options()) {\n'
886 ' do_set = false;\n'
887 ' switch(policy_proto.policy_options().mode()) {\n'
888 ' case em::PolicyOptions::MANDATORY:\n'
889 ' do_set = true;\n'
890 ' level = POLICY_LEVEL_MANDATORY;\n'
891 ' break;\n'
892 ' case em::PolicyOptions::RECOMMENDED:\n'
893 ' do_set = true;\n'
894 ' level = POLICY_LEVEL_RECOMMENDED;\n'
895 ' break;\n'
896 ' case em::PolicyOptions::UNSET:\n'
897 ' break;\n'
898 ' }\n'
899 ' }\n'
900 ' if (do_set) {\n')
901 f.write(' base::Value* value = %s;\n' %
902 (_CreateValue(policy.policy_type, 'policy_proto.value()')))
903 # TODO(bartfab): |value| == NULL indicates that the policy value could not be
904 # parsed successfully. Surface such errors in the UI.
905 f.write(' if (value) {\n')
906 f.write(' ExternalDataFetcher* external_data_fetcher = %s;\n' %
907 _CreateExternalDataFetcher(policy.policy_type, policy.name))
908 f.write(' map->Set(key::k%s, level, POLICY_SCOPE_USER,\n' %
909 policy.name)
910 f.write(' value, external_data_fetcher);\n'
911 ' }\n'
912 ' }\n'
913 ' }\n'
914 ' }\n')
917 def _WriteCloudPolicyDecoder(policies, os, f):
918 f.write(CPP_HEAD)
919 for policy in policies:
920 if policy.is_supported and not policy.is_device_only:
921 _WritePolicyCode(f, policy)
922 f.write(CPP_FOOT)
925 if __name__ == '__main__':
926 sys.exit(main())