blob: c1072acce054d09686fda5121006dc19ab5bc6fa [file] [log] [blame]
# Copyright 2018 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.
"""Blink feature-policy presubmit script.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
import os
def _dynamic_import(module_dir, module_name):
import sys
original_sys_path = sys.path
try:
sys.path = sys.path + [module_dir]
return __import__(module_name)
finally:
sys.path = original_sys_path
def _import_update_histogram_enum(input_api):
return _dynamic_import(
input_api.os_path.join(input_api.PresubmitLocalPath(), '..', '..',
'..', '..', '..', 'tools', 'metrics',
'histograms'), 'update_histogram_enum')
def _import_path_util(input_api):
return _dynamic_import(
input_api.os_path.join(input_api.PresubmitLocalPath(), '..', '..',
'..', '..', '..', 'tools', 'metrics', 'common'),
'path_util')
# Note: this function is copied from third_party/blink/renderer/build/scripts/json5_generator
def _json5_load(lines):
import re
import ast
# Use json5.loads when json5 is available. Currently we use simple
# regexs to convert well-formed JSON5 to PYL format.
# Strip away comments and quote unquoted keys.
re_comment = re.compile(r"^\s*//.*$|//+ .*$", re.MULTILINE)
re_map_keys = re.compile(r"^\s*([$A-Za-z_][\w]*)\s*:", re.MULTILINE)
pyl = re.sub(re_map_keys, r"'\1':", re.sub(re_comment, "", lines))
# Convert map values of true/false to Python version True/False.
re_true = re.compile(r":\s*true\b")
re_false = re.compile(r":\s*false\b")
pyl = re.sub(re_true, ":True", re.sub(re_false, ":False", pyl))
return ast.literal_eval(pyl)
def _json5_load_from_file(file_path):
with open(file_path, 'r') as f:
return _json5_load(f.read())
def uma_histogram_checks_factory(mojom_file, enum_name, update_script_file):
"""
:param mojom_file: str mojom_file name
:param enum_name: str enum name used in both mojom file and enums.xml
:param update_script_file: str update_script_file name
"""
def run_uma_histogram_checks(input_api, output_api):
source_path = ''
for f in input_api.AffectedFiles():
if f.LocalPath().endswith(mojom_file):
source_path = f.LocalPath()
break
else:
return []
start_marker = '^enum {} {{'.format(enum_name)
end_marker = '^};'
presubmit_error = _import_update_histogram_enum(
input_api).CheckPresubmitErrors(
histogram_enum_name=enum_name,
update_script_name=update_script_file,
source_enum_path=source_path,
start_marker=start_marker,
end_marker=end_marker,
strip_k_prefix=True)
if presubmit_error:
return [
output_api.PresubmitPromptWarning(presubmit_error,
items=[source_path])
]
return []
return run_uma_histogram_checks
def json5_config_checks_factory(mojom_source_path, json5_config_path,
enum_name, ignore_enums):
"""
:param mojom_source_path: str a src based path to mojom source file
:param json5_config_path: str a src based path to json5 config file
:param enum_name: str enum name used in mojom file
:param ignore_enums: Set[str] enum names that should be ignored in mojom file.
Enum names should not have 'k' prefixes here.
"""
def run_json5_config_checks(input_api, output_api):
# Note:
# |input_api.change.AffectedFiles()| returns all affected files in a CL.
# |input_api.AffectedFiles()| only returns affected files under current
# PRESUBMIT directory.
affected_paths = {
f.LocalPath()
for f in input_api.change.AffectedFiles()
}
if mojom_source_path not in affected_paths and json5_config_path not in affected_paths:
return []
mojom_enums = set(
_import_update_histogram_enum(input_api).ReadHistogramValues(
mojom_source_path,
start_marker='^enum {} {{'.format(enum_name),
end_marker='^};',
strip_k_prefix=True).values()) - ignore_enums
json5_enums = {
feature['name']
for feature in _json5_load_from_file(
_import_path_util(input_api).GetInputFile(json5_config_path))
['data']
}
json5_missing_enums = mojom_enums - json5_enums
mojom_missing_enums = json5_enums - mojom_enums
json5_messages = "{} are missing in json5 config.\n".format(
list(json5_missing_enums)) if json5_missing_enums else ""
mojom_messages = "{} are missing in mojom file.\n".format(
list(mojom_missing_enums)) if mojom_missing_enums else ""
return [] if json5_enums == mojom_enums else [
output_api.PresubmitPromptWarning(
"{} and {} are out of sync: {}{}".format(
json5_config_path, mojom_source_path, json5_messages,
mojom_messages),
items=[mojom_source_path, json5_config_path])
]
return run_json5_config_checks
checks = [
uma_histogram_checks_factory(
mojom_file="feature_policy_feature.mojom",
enum_name="FeaturePolicyFeature",
update_script_file="update_feature_policy_enum.py"),
uma_histogram_checks_factory(
mojom_file="document_policy_feature.mojom",
enum_name="DocumentPolicyFeature",
update_script_file="update_document_policy_enum.py"),
json5_config_checks_factory(
mojom_source_path=os.path.join('third_party', 'blink', 'public',
'mojom', 'feature_policy',
'feature_policy_feature.mojom'),
json5_config_path=os.path.join('third_party', 'blink', 'renderer',
'core', 'feature_policy',
'feature_policy_features.json5'),
enum_name="FeaturePolicyFeature",
ignore_enums={'NotFound'}),
json5_config_checks_factory(
mojom_source_path=os.path.join('third_party', 'blink', 'public',
'mojom', 'feature_policy',
'document_policy_feature.mojom'),
json5_config_path=os.path.join('third_party', 'blink', 'renderer',
'core', 'feature_policy',
'document_policy_features.json5'),
enum_name="DocumentPolicyFeature",
ignore_enums=set()),
]
def CheckChangeOnUpload(input_api, output_api): # pylint: disable=C0103
return [
message for check in checks
for message in check(input_api, output_api)
]
def CheckChangeOnCommit(input_api, output_api): # pylint: disable=C0103
return [
message for check in checks
for message in check(input_api, output_api)
]