1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)"""Updates ExtensionFunctions enum in histograms.xml file with values read from
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)extension_function_histogram_value.h.
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)If the file was pretty-printed, the updated version is pretty-printed too.
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)"""
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import logging
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import re
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import sys
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from xml.dom import minidom
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from diffutil import PromptUserToAcceptDiff
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from pretty_print import PrettyPrintNode
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)HISTOGRAMS_PATH = 'histograms.xml'
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ENUM_NAME = 'ExtensionFunctions'
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH = \
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '../../../chrome/browser/extensions/extension_function_histogram_value.h'
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ENUM_START_MARKER = "^enum HistogramValue {"
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ENUM_END_MARKER = "^ENUM_BOUNDARY"
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class UserError(Exception):
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def __init__(self, message):
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Exception.__init__(self, message)
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  @property
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def message(self):
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return self.args[0]
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def ExtractRegexGroup(line, regex):
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    m = re.match(regex, line)
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if m:
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return m.group(1)
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    else:
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return None
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def ReadHistogramValues(filename):
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  """Returns a list of pairs (label, value) corresponding to HistogramValue.
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Reads the extension_functions_histogram_value.h file, locates the
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  HistogramValue enum definition and returns a pair for each entry.
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  """
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Read the file as a list of lines
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  with open(filename) as f:
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content = f.readlines()
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Locate the enum definition and collect all entries in it
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  inside_enum = False # We haven't found the enum definition yet
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  result = []
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for line in content:
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    line = line.strip()
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if inside_enum:
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      # Exit condition: we reached last enum value
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if re.match(ENUM_END_MARKER, line):
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        inside_enum = False
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      else:
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        # Inside enum: generate new xml entry
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        label = ExtractRegexGroup(line.strip(), "^([\w]+)")
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if label:
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          result.append((label, enum_value))
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          enum_value += 1
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    else:
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if re.match(ENUM_START_MARKER, line):
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        inside_enum = True
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        enum_value = 0 # Start at 'UNKNOWN'
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return result
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def UpdateHistogramDefinitions(histogram_values, document):
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  """Sets the children of <enum name="ExtensionFunctions" ...> node in
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  |document| to values generated from policy ids contained in
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  |policy_templates|.
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Args:
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    histogram_values: A list of pairs (label, value) defining each extension
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      function
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    document: A minidom.Document object representing parsed histogram
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              definitions XML file.
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  """
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Find ExtensionFunctions enum.
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for enum_node in document.getElementsByTagName('enum'):
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if enum_node.attributes['name'].value == ENUM_NAME:
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        extension_functions_enum_node = enum_node
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        break
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  else:
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    raise UserError('No policy enum node found')
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Remove existing values.
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  while extension_functions_enum_node.hasChildNodes():
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    extension_functions_enum_node.removeChild(
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      extension_functions_enum_node.lastChild)
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Add a "Generated from (...)" comment
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  comment = ' Generated from {0} '.format(
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH)
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  extension_functions_enum_node.appendChild(document.createComment(comment))
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Add values generated from policy templates.
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (label, value) in histogram_values:
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    node = document.createElement('int')
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    node.attributes['value'] = str(value)
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    node.attributes['label'] = label
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    extension_functions_enum_node.appendChild(node)
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def Log(message):
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  logging.info(message)
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def main():
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if len(sys.argv) > 1:
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    print >>sys.stderr, 'No arguments expected!'
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    sys.stderr.write(__doc__)
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    sys.exit(1)
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Log('Reading histogram enum definition from "%s".'
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      % (EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH))
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  histogram_values = ReadHistogramValues(
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH)
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Log('Reading existing histograms from "%s".' % (HISTOGRAMS_PATH))
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  with open(HISTOGRAMS_PATH, 'rb') as f:
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    histograms_doc = minidom.parse(f)
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    f.seek(0)
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    xml = f.read()
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Log('Comparing histograms enum with new enum definition.')
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  UpdateHistogramDefinitions(histogram_values, histograms_doc)
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Log('Writing out new histograms file.')
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  new_xml = PrettyPrintNode(histograms_doc)
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if PromptUserToAcceptDiff(xml, new_xml, 'Is the updated version acceptable?'):
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    with open(HISTOGRAMS_PATH, 'wb') as f:
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      f.write(new_xml)
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Log('Done.')
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)if __name__ == '__main__':
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  main()
149