| Index: tools/ui/dialog_audit.py
|
| diff --git a/tools/ui/dialog_audit.py b/tools/ui/dialog_audit.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..7d7fa9cbb61e000b93ad58adf7ef033bc2efa552
|
| --- /dev/null
|
| +++ b/tools/ui/dialog_audit.py
|
| @@ -0,0 +1,200 @@
|
| +#!/usr/bin/env python
|
| +# Copyright 2017 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.
|
| +
|
| +"""
|
| +Systematically search the Chrome codebase for dialog invocations.
|
| +"""
|
| +
|
| +import subprocess
|
| +import sys
|
| +import re
|
| +import mmap
|
| +import os
|
| +import csv
|
| +
|
| +CLASS_UNKNOWN = '<class unknown>'
|
| +CLASS_UNKNOWN = '?'
|
| +VERBOSE = False
|
| +
|
| +BLACKLIST = {
|
| + 'ViewEventTestBase', # Has lots of uninteresting descendants in test files.
|
| + 'TestDelegate', # Causes infinite recursion.
|
| + 'UI' # Used in mash/webtest/webtest.cc - confuses regex.
|
| +}
|
| +
|
| +def InheritsRe(klass):
|
| + return r'public.*[: ]{}[, ]'.format(klass)
|
| +
|
| +def ChildRe(klass):
|
| + return r'class ([\w :]+)\s*: [^{]*' + klass + r'[^{]*[{]'
|
| +
|
| +def FindFilesInheritingFromClass(klass):
|
| + process = subprocess.Popen(['git', 'grep', '-l', InheritsRe(klass), '--',
|
| + '*.h', '*.mm', '*.cc'],
|
| + stdout=subprocess.PIPE)
|
| + return process.communicate()[0].splitlines()
|
| +
|
| +def ToLine(lines, offset):
|
| + line = 0
|
| + pos = 0
|
| + while pos < offset[0]:
|
| + pos += len(lines[line])
|
| + line += 1
|
| + return line
|
| +
|
| +def FindClassesInFilesInheritingFromClass(files, klass):
|
| + children = []
|
| + child_re = re.compile(ChildRe(klass), re.MULTILINE)
|
| + for filename in files:
|
| + with open(filename, 'r+') as f:
|
| + lines = f.readlines()
|
| + text = " ".join([l[:-1] for l in lines])
|
| + found = child_re.finditer(text)
|
| + before = len(children)
|
| + for child in found:
|
| + child_klass = child.group(1).split()[-1]
|
| + children.append((child_klass, filename, ToLine(lines, child.span()),
|
| + re.sub(' +', ' ', child.group(0)), klass))
|
| + if len(children) == before:
|
| + sys.stderr.write(
|
| + "ERROR: Couldn't find a child of {} in {}\n".format(klass,
|
| + filename))
|
| + return children
|
| +
|
| +def HuntForDecendants(result, parent, klasses, depth=''):
|
| + for klass in klasses:
|
| + if klass[0] in BLACKLIST:
|
| + sys.stderr.write('{}Skipping (blacklisted): {}\n'.format(depth, klass[0]))
|
| + continue
|
| +
|
| + result.append(klass)
|
| + sys.stderr.write('{}{}'.format(depth, klass[0]))
|
| + files = FindFilesInheritingFromClass(klass[0])
|
| + children = FindClassesInFilesInheritingFromClass(files, klass[0])
|
| + sys.stderr.write( ' has {} children\n'.format(len(children)))
|
| + HuntForDecendants(result, klass, children, depth + ' ')
|
| +
|
| +def DefaultHunt():
|
| + seed = 'WidgetDelegate'
|
| + #$seed = 'DialogDelegate'
|
| + #seed = 'DialogDelegateView'
|
| + #seed = 'LocationBarBubbleDelegateView'
|
| + result = []
|
| + HuntForDecendants(result, 'Seed', [(seed, '', 0, '', 'Seed')])
|
| + return result
|
| +
|
| +def ToUrl(filename, klass, line):
|
| + return 'https://cs.chromium.org/chromium/src/{}?q={}&l={}'.format(
|
| + filename, klass, line)
|
| +
|
| +def Hyperlink(klass, url):
|
| + return '=HYPERLINK("{}", "{}")'.format(url, klass)
|
| +
|
| +def Process(data):
|
| + cells = eval("".join(open('tools/ui/cells.cache').readlines()))
|
| + newcells = []
|
| + cellmap = {}
|
| + for row in range(len(cells)):
|
| + ux_name = cells[row][1]
|
| + klass = cells[row][0]
|
| + if klass == '' and ux_name != '':
|
| + newcells.append([CLASS_UNKNOWN, ux_name])
|
| + print "Unmatched: {} {}".format(row, ux_name)
|
| + else:
|
| + cellmap.setdefault(klass, []).append(row)
|
| + newcells.append([klass, ux_name])
|
| + print ""
|
| + ignored = []
|
| + parents = {}
|
| + missing = {}
|
| + dupe_check = {}
|
| + for (klass, filename, line, match, parent) in data:
|
| + # Ignore test files
|
| + if filename.find('test.') > 0:
|
| + ignored.append((klass, filename))
|
| + continue
|
| +
|
| + dupe_check.setdefault(klass, []).append(filename)
|
| + if len(dupe_check[klass]) > 1:
|
| + print "WARNING: {} found in {}".format(klass, dupe_check[klass])
|
| +
|
| + # Track classes that have children and ignore them.
|
| + parents.setdefault(parent, [('f', 'c', 0, 'p'), []])[1].append(klass)
|
| + if parent in missing:
|
| + parents[parent][0] = missing[parent]
|
| + del missing[parent]
|
| +
|
| + url = ToUrl(filename, klass, line)
|
| + if klass in cellmap:
|
| + for row in cellmap[klass]:
|
| + newcells[row][0] = Hyperlink(klass, url)
|
| + newcells[row].append(filename)
|
| + newcells[row].append(parent)
|
| + if VERBOSE:
|
| + print 'Matched: {}'.format(klass)
|
| + continue
|
| +
|
| + missing[klass] = (filename, klass, line, parent)
|
| +
|
| + sys.stderr.write('\nIgnored (Tests):')
|
| + for ignore in ignored:
|
| + sys.stderr.write(' {}({})'.format(ignore[0], os.path.basename(ignore[1])))
|
| + sys.stderr.write('\n')
|
| +
|
| + sys.stderr.write('\nIgnored (Parents):')
|
| + for pk in parents:
|
| + parent = parents[pk]
|
| + filename, klass, line, p = parent[0]
|
| + children = " ".join(parent[1])
|
| + basename = os.path.basename(filename)
|
| + sys.stderr.write('\n\t {}({}): {}'.format(pk, basename, children))
|
| + sys.stderr.write('\n')
|
| +
|
| + # Sort by filename.
|
| + missing_list = missing.values()
|
| + missing_list.sort()
|
| +
|
| + # Write out CSV
|
| + with open('tools/ui/sheet.csv', 'wb') as csvfile:
|
| + writer = csv.writer(csvfile, dialect='excel-tab')
|
| + for row in range(len(cells)):
|
| + if row == 0:
|
| + writer.writerow(['DialogClass', 'Description', 'File', 'ParentClass',
|
| + 'Children'])
|
| + continue
|
| + writer.writerow(newcells[row])
|
| +
|
| + for m in missing_list:
|
| + filename, klass, line, parent = m
|
| + url = ToUrl(filename, klass, line)
|
| + missing_text = 'What is this?'
|
| + writer.writerow([Hyperlink(klass, url), missing_text, filename, parent])
|
| + print 'Missing: {:40} from {}'.format(klass, url)
|
| +
|
| + for pk in parents:
|
| + filename, klass, line, parent = parents[pk][0]
|
| + url = ToUrl(filename, klass, line)
|
| + num_children = len(parents[pk][1])
|
| + parent_text = 'Has {} Child{}'.format(num_children,
|
| + num_children != 1 and "ren" or "")
|
| + writer.writerow([Hyperlink(klass, url), parent_text, filename, parent,
|
| + ",".join(parents[pk][1])])
|
| + for ignore in ignored:
|
| + writer.writerow([ignore[0], '<test>', ignore[1]])
|
| +
|
| +def main():
|
| + try:
|
| + data = eval(open('tools/ui/dialog_audit.cache').readline())
|
| + Process(data)
|
| + except IOError as e:
|
| + data = DefaultHunt()
|
| + print data
|
| + sys.stderr.write("""
|
| +Try `tools/ui/dialog_audit.py > tools/ui/dialog_audit.cache.new && \
|
| +mv tools/ui/dialog_audit.cache.new tools/ui/dialog_audit.cache`
|
| +""")
|
| +
|
| +if __name__ == '__main__':
|
| + sys.exit(main())
|
|
|