# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Updates MappedEditingCommands enum in histograms.xml file with values read from EditorCommand.cpp. If the file was pretty-printed, the updated version is pretty-printed too. """ import logging import os import re import sys from xml.dom import minidom from diffutil import PromptUserToAcceptDiff import print_style # Import the metrics/common module. sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) from diff_util import PromptUserToAcceptDiff HISTOGRAMS_PATH = 'histograms.xml' ENUM_NAME = 'MappedEditingCommands' EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH = \ '../../../third_party/WebKit/Source/core/editing/EditorCommand.cpp' ENUM_START_MARKER = "^ static const CommandEntry commands\[\] = {" ENUM_END_MARKER = "^ };" class UserError(Exception): def __init__(self, message): Exception.__init__(self, message) @property def message(self): return self.args[0] def ReadHistogramValues(filename): """Returns a list of pairs (label, value) corresponding to HistogramValue. Reads the EditorCommand.cpp file, locates the HistogramValue enum definition and returns a pair for each entry. """ # Read the file as a list of lines with open(filename) as f: content = f.readlines() # Locate the enum definition and collect all entries in it inside_enum = False # We haven't found the enum definition yet result = [] for line in content: if inside_enum: # Exit condition: we reached last enum value if re.match(ENUM_END_MARKER, line): inside_enum = False else: # Inside enum: generate new xml entry m = re.match("^{ \"([\w]+)\", \{([\w]+)", line.strip()) if m: result.append((m.group(1), int(m.group(2)))) else: if re.match(ENUM_START_MARKER, line): inside_enum = True enum_value = 0 # Start at 'UNKNOWN' return sorted(result, key=lambda pair: pair[1]) def UpdateHistogramDefinitions(histogram_values, document): """Sets the children of node in |document| to values generated from policy ids contained in |policy_templates|. Args: histogram_values: A list of pairs (label, value) defining each extension function document: A minidom.Document object representing parsed histogram definitions XML file. """ # Find ExtensionFunctions enum. for enum_node in document.getElementsByTagName('enum'): if enum_node.attributes['name'].value == ENUM_NAME: extension_functions_enum_node = enum_node break else: raise UserError('No policy enum node found') # Remove existing values. while extension_functions_enum_node.hasChildNodes(): extension_functions_enum_node.removeChild( extension_functions_enum_node.lastChild) # Add a "Generated from (...)" comment comment = ' Generated from {0} '.format( EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH) extension_functions_enum_node.appendChild(document.createComment(comment)) # Add values generated from policy templates. for (label, value) in histogram_values: node = document.createElement('int') node.attributes['value'] = str(value) node.attributes['label'] = label extension_functions_enum_node.appendChild(node) def Log(message): logging.info(message) def main(): if len(sys.argv) > 1: print >>sys.stderr, 'No arguments expected!' sys.stderr.write(__doc__) sys.exit(1) Log('Reading histogram enum definition from "%s".' % (EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH)) histogram_values = ReadHistogramValues( EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH) Log('Reading existing histograms from "%s".' % (HISTOGRAMS_PATH)) with open(HISTOGRAMS_PATH, 'rb') as f: histograms_doc = minidom.parse(f) f.seek(0) xml = f.read() Log('Comparing histograms enum with new enum definition.') UpdateHistogramDefinitions(histogram_values, histograms_doc) Log('Writing out new histograms file.') new_xml = print_style.GetPrintStyle().PrettyPrintNode(histograms_doc) if PromptUserToAcceptDiff(xml, new_xml, 'Is the updated version acceptable?'): with open(HISTOGRAMS_PATH, 'wb') as f: f.write(new_xml) Log('Done.') if __name__ == '__main__': main()