Index: chrome/browser/resources/safe_browsing/gen_file_type_proto.py |
diff --git a/chrome/browser/resources/safe_browsing/gen_file_type_proto.py b/chrome/browser/resources/safe_browsing/gen_file_type_proto.py |
index a071b529fe8b904969126f7a7fd4df969d38a889..1e55f9a10c140b543f45f5c6214492ca03ab8884 100755 |
--- a/chrome/browser/resources/safe_browsing/gen_file_type_proto.py |
+++ b/chrome/browser/resources/safe_browsing/gen_file_type_proto.py |
@@ -5,24 +5,129 @@ |
""" |
Convert the ASCII download_file_types.asciipb proto into a binary resource. |
+ |
+ We generate a separate variant of the binary proto for each platform, |
+ each which contains only the values that platform needs. |
""" |
import optparse |
+import re |
import subprocess |
import sys |
-def ConvertProto(opts): |
- # Find the proto code |
- for path in opts.path: |
+def ImportProtoModules(paths): |
+ """Import the protobuf modiles we need. |paths| is list of import paths""" |
+ for path in paths: |
# Put the path to our proto libraries in front, so that we don't use system |
# protobuf. |
sys.path.insert(1, path) |
- |
import download_file_types_pb2 as config_pb2 |
+ globals()['config_pb2'] = config_pb2 |
+ |
import google.protobuf.text_format as text_format |
+ globals()['text_format'] = text_format |
+ |
+ |
+# Map of platforms for which we can generate binary protos. |
+# This must be run after the custom imports. |
+# key: type-name |
+# value: proto-platform_type (int) |
+def PlatformTypes(): |
+ return { |
+ "android": config_pb2.DownloadFileType.PLATFORM_ANDROID, |
+ "chromeos": config_pb2.DownloadFileType.PLATFORM_CHROME_OS, |
+ "linux": config_pb2.DownloadFileType.PLATFORM_LINUX, |
+ "mac": config_pb2.DownloadFileType.PLATFORM_MAC, |
+ "win": config_pb2.DownloadFileType.PLATFORM_WINDOWS, |
+ } |
+ |
+ |
+def ValidatePb(pb): |
+ """ Validate the basic values of the protobuf. The |
+ file_type_policies_unittest.cc will also validate it by platform, |
+ but this will catch errors earlier. |
+ """ |
+ assert pb.version_id > 0; |
+ assert pb.sampled_ping_probability >= 0.0; |
+ assert pb.sampled_ping_probability <= 1.0; |
+ assert len(pb.default_file_type.platform_settings) >= 1; |
+ assert len(pb.file_types) > 1; |
+ |
+ |
+def PrunePlatformSettings(file_type, default_settings, platform_type): |
+ # Modify this file_type's platform_settings by keeping the only the |
+ # best one for this platform_type. In order of preference: |
+ # * Exact match to platform_type |
+ # * PLATFORM_ANY entry |
+ # * or copy from the default file type. |
+ |
+ last_platform = -1 |
+ setting_match = None |
+ for s in file_type.platform_settings: |
+ # Enforce: sorted and no dups (signs of mistakes). |
+ assert last_platform < s.platform, ( |
+ "Extension '%s' has duplicate or out of order platform: '%s'" % |
+ (file_type.extension, s.platform)) |
+ last_platform = s.platform |
+ |
+ # Pick the most specific match. |
+ if ((s.platform == platform_type) or |
+ (s.platform == config_pb2.DownloadFileType.PLATFORM_ANY and |
+ setting_match is None)): |
+ setting_match = s |
+ |
+ # If platform_settings was empty, we'll fill in from the default |
+ if setting_match is None: |
+ assert default_settings is not None, ( |
+ "Missing default settings for platform %d" % platform_type) |
+ setting_match = default_settings |
+ |
+ # Now clear out the full list and replace it with 1 entry. |
+ del file_type.platform_settings[:] |
+ new_setting = file_type.platform_settings.add() |
+ new_setting.CopyFrom(setting_match) |
+ new_setting.ClearField('platform') |
+ |
+ |
+def FilterPbForPlatform(full_pb, platform_type): |
+ """ Return a filtered protobuf for this platform_type """ |
+ new_pb = config_pb2.DownloadFileTypeConfig(); |
+ new_pb.CopyFrom(full_pb) |
+ |
+ # Ensure there's only one platform_settings for the default. |
+ PrunePlatformSettings(new_pb.default_file_type, None, platform_type) |
+ |
+ # This can be extended if we want to match weird extensions. |
+ # Just no dots, non-UTF8, or uppercase chars. |
+ invalid_char_re = re.compile('[^a-z0-9_-]') |
+ |
+ # Filter platform_settings for each type. |
+ uma_values_used = set() |
+ extensions_used = set() |
+ for file_type in new_pb.file_types: |
+ assert not invalid_char_re.search(file_type.extension), ( |
+ "File extension '%s' contains non alpha-num-dash chars" % ( |
+ file_type.extension)) |
+ assert file_type.extension is not extensions_used, ( |
+ "Duplicate extension '%s'" % file_type.extension) |
+ extensions_used.add(file_type.extension) |
+ |
+ assert file_type.uma_value not in uma_values_used, ( |
+ "Extension '%s' reused UMA value %d." % ( |
+ file_type.extension, file_type.uma_value)) |
+ uma_values_used.add(file_type.uma_value) |
+ |
+ # Modify file_type to include only the best match platform_setting. |
+ PrunePlatformSettings( |
+ file_type, new_pb.default_file_type.platform_settings[0], platform_type) |
+ |
+ return new_pb |
+ |
+ |
+def GeneratBinaryProtos(opts): |
# Read the ASCII |
ifile = open(opts.infile, 'r') |
ascii_pb_str = ifile.read() |
@@ -32,8 +137,12 @@ def ConvertProto(opts): |
pb = config_pb2.DownloadFileTypeConfig() |
text_format.Merge(ascii_pb_str, pb) |
+ ValidatePb(pb); |
+ platform_type = PlatformTypes()[opts.type] |
+ filtered_pb = FilterPbForPlatform(pb, platform_type); |
+ |
# Serialize it |
- binary_pb_str = pb.SerializeToString() |
+ binary_pb_str = filtered_pb.SerializeToString() |
# Write it to disk |
open(opts.outfile, 'wb').write(binary_pb_str) |
@@ -46,10 +155,13 @@ def main(): |
help='Wrap this script in another python ' |
'execution to disable site-packages. This is a ' |
'fix for http://crbug.com/605592') |
+ parser.add_option('-t', '--type', |
+ help='The platform type. One of android, chromeos, ' + |
+ 'linux, mac, win') |
parser.add_option('-i', '--infile', |
help='The ASCII DownloadFileType-proto file to read.') |
parser.add_option('-o', '--outfile', |
- help='The binary file to write.') |
+ help='The binary file to write to.') |
parser.add_option('-p', '--path', action="append", |
help='Repeat this as needed. Directory(s) containing ' + |
'the download_file_types_pb2.py and ' + |
@@ -62,18 +174,27 @@ def main(): |
if opts.wrap: |
# Run this script again with different args to the interpreter. |
command = [sys.executable, '-S', '-s', sys.argv[0]] |
+ command += ['-t', opts.type] |
command += ['-i', opts.infile] |
command += ['-o', opts.outfile] |
for path in opts.path: |
command += ['-p', path] |
sys.exit(subprocess.call(command)) |
+ ImportProtoModules(opts.path) |
+ |
+ if (opts.type not in PlatformTypes()): |
+ print "ERROR: Unknown platform type '%s'" % opts.type |
+ parser.print_help() |
+ return 1 |
+ |
try: |
- ConvertProto(opts) |
+ GeneratBinaryProtos(opts) |
except Exception as e: |
- print "ERROR: %s failed to parse ASCII proto\n%s: %s\n" % ( |
- sys.argv, opts.infile, str(e)) |
+ print "ERROR: Failed to render binary version of %s:\n %s\n" % ( |
+ opts.infile, str(e)) |
return 1 |
+ |
if __name__ == '__main__': |
sys.exit(main()) |