# Copyright (c) 2013 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. """Top-level presubmit script for Blink. See https://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for more details about the presubmit API built into gcl. """ import imp import inspect import os import re try: # pylint: disable=C0103 audit_non_blink_usage = imp.load_source( 'audit_non_blink_usage', os.path.join(os.path.dirname(inspect.stack()[0][1]), 'tools/blinkpy/presubmit/audit_non_blink_usage.py')) except IOError: # One of the presubmit upload tests tries to exec this script, which doesn't interact so well # with the import hack... just ignore the exception here and hope for the best. pass _EXCLUDED_PATHS = ( # These are third-party dependencies that we don't directly control. r'^third_party[\\/]blink[\\/]tools[\\/]blinkpy[\\/]third_party[\\/]wpt[\\/]wpt[\\/].*', r'^third_party[\\/]blink[\\/]web_tests[\\/]external[\\/]wpt[\\/]tools[\\/].*', r'^third_party[\\/]blink[\\/]web_tests[\\/]external[\\/]wpt[\\/]resources[\\/]webidl2[\\/].*', ) def _CheckForWrongMojomIncludes(input_api, output_api): # In blink the code should either use -blink.h or -shared.h mojom # headers, except in public where only -shared.h headers should be # used to avoid exporting Blink types outside Blink. def source_file_filter(path): return input_api.FilterSourceFile( path, black_list=[ r'third_party/blink/common/', r'third_party/blink/public/common', r'third_party/blink/renderer/platform/loader/fetch/url_loader', ]) pattern = input_api.re.compile(r'#include\s+[<"](.+)\.mojom(.*)\.h[>"]') public_folder = input_api.os_path.normpath('third_party/blink/public/') non_blink_mojom_errors = [] public_blink_mojom_errors = [] # Allow including specific non-blink interfaces that are used in the public C++ API. # Adding to these allowed interfaces should meet the following conditions: # - Its pros/cons is discussed and have consensus on platform-architecture-dev@ and/or # - It uses POD types that will not import STL (or base string) types into blink # (such as no strings or vectors). # # So far, non-blink interfaces are allowed only for loading / loader and media # interfaces so that we don't need type conversions to get through the # boundary between Blink and non-Blink. allowed_interfaces = ('services/network/public/mojom/cross_origin_embedder_policy', 'services/network/public/mojom/fetch_api', 'services/network/public/mojom/load_timing_info', 'third_party/blink/public/mojom/worker/subresource_loader_updater', 'media/mojo/mojom/interface_factory', 'media/mojo/mojom/video_decoder') for f in input_api.AffectedFiles(file_filter=source_file_filter): for line_num, line in f.ChangedContents(): error_list = None match = pattern.match(line) if (match and match.group(1) not in allowed_interfaces): if match.group(2) not in ('-shared', '-forward'): if f.LocalPath().startswith(public_folder): error_list = public_blink_mojom_errors elif match.group(2) not in ('-blink', '-blink-forward', '-blink-test-utils'): # Neither -shared.h, -blink.h, -blink-forward.h nor -blink-test-utils.h. error_list = non_blink_mojom_errors if error_list is not None: error_list.append(' %s:%d %s' % ( f.LocalPath(), line_num, line)) results = [] if non_blink_mojom_errors: results.append(output_api.PresubmitError( 'Files that include non-Blink variant mojoms found. ' 'You must include .mojom-blink.h, .mojom-forward.h or .mojom-shared.h instead:', non_blink_mojom_errors)) if public_blink_mojom_errors: results.append(output_api.PresubmitError( 'Public blink headers using Blink variant mojoms found. ' 'You must include .mojom-forward.h or .mojom-shared.h instead:', public_blink_mojom_errors)) return results def _CommonChecks(input_api, output_api): """Checks common to both upload and commit.""" # We should figure out what license checks we actually want to use. license_header = r'.*' results = [] results.extend(input_api.canned_checks.PanProjectChecks( input_api, output_api, excluded_paths=_EXCLUDED_PATHS, maxlen=800, license_header=license_header)) results.extend(_CheckForWrongMojomIncludes(input_api, output_api)) return results def _FilterPaths(input_api): """Returns input files with certain paths removed.""" files = [] for f in input_api.AffectedFiles(): file_path = f.LocalPath() # Filter out changes in web_tests/. if ('web_tests' + input_api.os_path.sep in file_path and 'TestExpectations' not in file_path): continue if '/PRESUBMIT' in file_path: continue # Skip files that were generated by bison. if re.search('third_party/blink/renderer/' + 'core/xml/xpath_grammar_generated\.(cc|h)$', file_path): continue files.append(input_api.os_path.join('..', '..', file_path)) return files def _CheckStyle(input_api, output_api): files = _FilterPaths(input_api) # Do not call check_blink_style.py with empty affected file list if all # input_api.AffectedFiles got filtered. if not files: return [] style_checker_path = input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools', 'check_blink_style.py') args = [input_api.python_executable, style_checker_path, '--diff-files'] args += files results = [] try: child = input_api.subprocess.Popen(args, stderr=input_api.subprocess.PIPE) _, stderrdata = child.communicate() if child.returncode != 0: results.append(output_api.PresubmitError( 'check_blink_style.py failed', [stderrdata])) except Exception as e: results.append(output_api.PresubmitNotifyResult( 'Could not run check_blink_style.py', [str(e)])) return results def _CheckForPrintfDebugging(input_api, output_api): """Generally speaking, we'd prefer not to land patches that printf debug output. """ printf_re = input_api.re.compile(r'^\s*(printf\(|fprintf\(stderr,)') errors = input_api.canned_checks._FindNewViolationsOfRule( lambda _, x: not printf_re.search(x), input_api, None) errors = [' * %s' % violation for violation in errors] if errors: return [output_api.PresubmitPromptOrNotify( 'printf debugging is best debugging! That said, it might ' 'be a good idea to drop the following occurences from ' 'your patch before uploading:\n%s' % '\n'.join(errors))] return [] def _CheckForForbiddenChromiumCode(input_api, output_api): """Checks that Blink uses Chromium classes and namespaces only in permitted code.""" # TODO(dcheng): This is pretty similar to _FindNewViolationsOfRule. Unfortunately, that can;'t # be used directly because it doesn't give the callable enough information (namely the path to # the file), so we duplicate the logic here... results = [] for f in input_api.AffectedFiles(): path = f.LocalPath() errors = audit_non_blink_usage.check(path, [(i + 1, l) for i, l in enumerate(f.NewContents())]) if errors: errors = audit_non_blink_usage.check(path, f.ChangedContents()) if errors: for error in errors: msg = '%s:%d uses disallowed identifier %s' % ( path, error.line, error.identifier) if error.advice: msg += ". Advice: %s" % "\n".join(error.advice) if error.warning: results.append(output_api.PresubmitPromptWarning(msg)) else: results.append(output_api.PresubmitError(msg)) return results def CheckChangeOnUpload(input_api, output_api): results = [] results.extend(_CommonChecks(input_api, output_api)) results.extend(_CheckStyle(input_api, output_api)) results.extend(_CheckForPrintfDebugging(input_api, output_api)) results.extend(_CheckForForbiddenChromiumCode(input_api, output_api)) return results def CheckChangeOnCommit(input_api, output_api): results = [] results.extend(_CommonChecks(input_api, output_api)) results.extend(input_api.canned_checks.CheckTreeIsOpen( input_api, output_api, json_url='http://chromium-status.appspot.com/current?format=json')) results.extend(input_api.canned_checks.CheckChangeHasDescription( input_api, output_api)) return results