# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Presubmit script for Android Java code.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
This presubmit checks for the following:
- No new calls to Notification.Builder or NotificationCompat.Builder
constructors. Callers should use NotificationWrapperBuilder instead.
- No new calls to AlertDialog.Builder. Callers should use ModalDialogView
instead.
"""
import re
NEW_NOTIFICATION_BUILDER_RE = re.compile(
r'\bnew\sNotification(Compat)?\.Builder\b')
IMPORT_APP_COMPAT_ALERTDIALOG_RE = re.compile(
r'\bimport\sandroid\.support\.v7\.app\.AlertDialog;')
NEW_COMPATIBLE_ALERTDIALOG_BUILDER_RE = re.compile(
r'\bnew\s+(UiUtils\s*\.)?CompatibleAlertDialogBuilder\b')
NEW_ALERTDIALOG_BUILDER_RE = re.compile(
r'\bnew\sAlertDialog\.Builder\b')
SPLIT_COMPAT_UTILS_IMPL_NAME_RE = re.compile(
r'\bBundleUtils\.getIdentifierName\(\s*[^\s"]')
COMMENT_RE = re.compile(r'^\s*(//|/\*|\*)')
BROWSER_ROOT = 'chrome/android/java/src/org/chromium/chrome/browser/'
def CheckChangeOnUpload(input_api, output_api):
return _CommonChecks(input_api, output_api)
def CheckChangeOnCommit(input_api, output_api):
return _CommonChecks(input_api, output_api)
def _CommonChecks(input_api, output_api):
"""Checks common to both upload and commit."""
result = []
result.extend(_CheckNotificationConstructors(input_api, output_api))
result.extend(_CheckAlertDialogBuilder(input_api, output_api))
result.extend(_CheckCompatibleAlertDialogBuilder(input_api, output_api))
result.extend(_CheckBundleUtilsIdentifierName(input_api, output_api))
# Add more checks here
return result
def _CheckNotificationConstructors(input_api, output_api):
files_to_skip = (
'chrome/android/java/src/org/chromium/chrome/browser/notifications/'
'ChromeNotificationWrapperBuilder.java',
'chrome/android/java/src/org/chromium/chrome/browser/notifications/'
'ChromeNotificationWrapperCompatBuilder.java'
)
error_msg = '''
Android Notification Construction Check failed:
Your new code added one or more calls to the Notification.Builder and/or
NotificationCompat.Builder constructors, listed below.
This is banned, please construct notifications using
NotificationWrapperBuilderFactory.createNotificationWrapperBuilder instead,
specifying a channel for use on Android O.
See https://crbug.com/678670 for more information.
'''
return _CheckReIgnoreComment(input_api, output_api, error_msg, files_to_skip,
NEW_NOTIFICATION_BUILDER_RE)
def _CheckAlertDialogBuilder(input_api, output_api):
# In general, preference and FRE related UIs are not relevant to VR mode.
files_to_skip = (
BROWSER_ROOT + 'autofill/prefeditor/EditorDialog.java',
BROWSER_ROOT + 'browserservices/ClearDataDialogActivity.java',
BROWSER_ROOT + 'browsing_data/ConfirmImportantSitesDialogFragment.java',
BROWSER_ROOT + 'browsing_data/OtherFormsOfHistoryDialogFragment.java',
BROWSER_ROOT + 'dom_distiller/DistilledPagePrefsView.java',
BROWSER_ROOT + 'dom_distiller/DomDistillerUIUtils.java',
BROWSER_ROOT + 'download/OMADownloadHandler.java',
BROWSER_ROOT + 'init/LaunchFailedActivity.java',
BROWSER_ROOT + 'password_manager/AccountChooserDialog.java',
BROWSER_ROOT + 'password_manager/AutoSigninFirstRunDialog.java',
# TODO(crbug.com/40945893): Tentatively suppressed.
(BROWSER_ROOT +
'password_manager/settings/ExportErrorDialogFragment.java'),
(BROWSER_ROOT +
'password_manager/settings/ExportWarningDialogFragment.java'),
(BROWSER_ROOT +
'password_manager/settings/ProgressBarDialogFragment.java'),
# end of https://crbug.com/1505284
BROWSER_ROOT + r'settings[\\\/].*',
BROWSER_ROOT + 'site_settings/AddExceptionPreference.java',
BROWSER_ROOT + 'site_settings/ChosenObjectSettings.java',
BROWSER_ROOT + 'site_settings/ManageSpaceActivity.java',
BROWSER_ROOT + 'site_settings/ManageSpaceActivity.java',
BROWSER_ROOT + 'site_settings/SingleCategorySettings.java',
BROWSER_ROOT + 'site_settings/SingleWebsiteSettings.java',
BROWSER_ROOT + 'sync/settings/ManageSyncSettings.java',
BROWSER_ROOT + 'sync/settings/SyncAndServicesSettings.java',
BROWSER_ROOT + 'sync/ui/PassphraseCreationDialogFragment.java',
BROWSER_ROOT + 'sync/ui/PassphraseDialogFragment.java',
BROWSER_ROOT + 'sync/ui/PassphraseTypeDialogFragment.java',
BROWSER_ROOT + 'webapps/WebApkOfflineDialog.java',
)
error_msg = '''
AlertDialog.Builder Check failed:
Your new code added one or more calls to the AlertDialog.Builder, listed
below.
We recommend you use ModalDialogProperties to show a dialog whenever possible
to support VR mode. You could only keep the AlertDialog if you are certain
that your new AlertDialog is not used in VR mode (e.g. pereference, FRE)
If you are in doubt, contact
//src/chrome/android/java/src/org/chromium/chrome/browser/vr/VR_JAVA_OWNERS
'''
error_files = []
result = _CheckReIgnoreComment(input_api, output_api, error_msg,
files_to_skip, NEW_ALERTDIALOG_BUILDER_RE,
error_files)
wrong_builder_errors = []
wrong_builder_error_msg = '''
Android Use of AppCompat AlertDialog.Builder Check failed:
Your new code added one or more calls to the AppCompat AlertDialog.Builder,
file listed below.
If you are keeping the new AppCompat AlertDialog.Builder, please use
CompatibleAlertDialogBuilder instead to work around support library issues.
See https://crbug.com/966101 for more information.
'''
for f in error_files:
contents = input_api.ReadFile(f)
if IMPORT_APP_COMPAT_ALERTDIALOG_RE.search(contents):
wrong_builder_errors.append(' %s' % (f.LocalPath()))
if wrong_builder_errors:
result.extend([output_api.PresubmitError(
wrong_builder_error_msg, wrong_builder_errors)])
return result
def _CheckCompatibleAlertDialogBuilder(input_api, output_api):
files_to_skip = (
BROWSER_ROOT + 'autofill/keyboard_accessory/'
'AutofillKeyboardAccessoryBridge.java',
BROWSER_ROOT + 'dom_distiller/DistilledPagePrefsView.java',
BROWSER_ROOT + 'dom_distiller/DomDistillerUIUtils.java',
BROWSER_ROOT + 'download/DownloadController.java',
BROWSER_ROOT + 'download/OMADownloadHandler.java',
BROWSER_ROOT + 'externalnav/ExternalNavigationDelegateImpl.java',
BROWSER_ROOT + 'payments/AndroidPaymentApp.java',
BROWSER_ROOT + 'permissions/AndroidPermissionRequester.java',
BROWSER_ROOT + 'share/ShareDelegateImpl.java',
BROWSER_ROOT + 'util/AccessibilityUtil.java',
BROWSER_ROOT + 'webapps/AddToHomescreenDialog.java',
BROWSER_ROOT + 'webapps/WebappOfflineDialog.java',
)
error_msg = '''
Android Use of CompatibleAlertDialogBuilder Check failed:
Your new code added one or more calls to the CompatibleAlertDialogBuilder
constructors, listed below.
We recommend you use ModalDialogProperties to show a dialog whenever possible
to support VR mode. You could only keep the AlertDialog if you are certain
that your new AlertDialog is not used in VR mode (e.g. pereference, FRE)
If you are in doubt, contact
//src/chrome/android/java/src/org/chromium/chrome/browser/vr/VR_JAVA_OWNERS
'''
return _CheckReIgnoreComment(input_api, output_api, error_msg, files_to_skip,
NEW_COMPATIBLE_ALERTDIALOG_BUILDER_RE)
def _CheckBundleUtilsIdentifierName(input_api, output_api):
error_msg = '''
BundleUtils.getIdentifierName() not check failed:
BundleUtils.getIdentifierName() must be called with a String literal,
otherwise R8 may not correctly obfuscate the class name passed in.
'''
return _CheckReIgnoreComment(input_api, output_api, error_msg, [],
SPLIT_COMPAT_UTILS_IMPL_NAME_RE)
def _CheckReIgnoreComment(input_api, output_api, error_msg, files_to_skip,
regular_expression, error_files=None):
def CheckLine(current_file, line_number, line, problems, error_files):
"""Returns a boolean whether the line contains an error."""
if (regular_expression.search(line) and not COMMENT_RE.search(line)):
if error_files is not None:
error_files.append(current_file)
problems.append(
' %s:%d\n \t%s' %
(current_file.LocalPath(), line_number, line.strip()))
return True
return False
problems = []
sources = lambda x: input_api.FilterSourceFile(
x, files_to_check=(r'.*\.java$',), files_to_skip=files_to_skip)
for f in input_api.AffectedFiles(include_deletes=False,
file_filter=sources):
previous_line = ''
for line_number, line in enumerate(f.NewContents(), start=1):
if not CheckLine(f, line_number, line, problems, error_files):
if previous_line:
two_lines = '\n'.join([previous_line, line])
CheckLine(f, line_number, two_lines, problems, error_files)
previous_line = line
else:
previous_line = ''
if problems:
return [output_api.PresubmitError(error_msg, problems)]
return []