| # Copyright 2014 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| Presubmit for Chromium HTML resources. See chrome/browser/PRESUBMIT.py. |
| """ |
| |
| from . import regex_check |
| |
| |
| class HtmlChecker(object): |
| def __init__(self, input_api, output_api, file_filter=None): |
| self.input_api = input_api |
| self.output_api = output_api |
| self.file_filter = file_filter |
| |
| def ClassesUseDashFormCheck(self, line_number, line): |
| msg = "Classes should use dash-form." |
| re = self.input_api.re |
| class_regex = re.compile(""" |
| (?:^|\s) # start of line or whitespace |
| (class="[^"]*[A-Z_][^"]*") # class contains caps or '_' |
| """, |
| re.VERBOSE) |
| |
| # $i18n{...} messes with highlighting. Special path for this. |
| if "$i18n{" in line: |
| match = re.search(class_regex, re.sub("\$i18n{[^}]+}", "", line)) |
| return " line %d: %s" % (line_number, msg) if match else "" |
| |
| return regex_check.RegexCheck(re, line_number, line, class_regex, msg) |
| |
| def DoNotCloseSingleTagsCheck(self, line_number, line): |
| regex = r"(/>)" |
| return regex_check.RegexCheck(self.input_api.re, line_number, line, regex, |
| "Do not close single tags.") |
| |
| def DoNotUseBrElementCheck(self, line_number, line): |
| regex = r"(<br\b)" |
| return regex_check.RegexCheck(self.input_api.re, line_number, line, regex, |
| "Do not use <br>; place blocking elements (<div>) as appropriate.") |
| |
| def DoNotUseInputTypeButtonCheck(self, line_number, line): |
| regex = self.input_api.re.compile(""" |
| (<input [^>]* # "<input " followed by anything but ">" |
| type="button" # type="button" |
| [^>]*>) # anything but ">" then ">" |
| """, |
| self.input_api.re.VERBOSE) |
| return regex_check.RegexCheck(self.input_api.re, line_number, line, regex, |
| 'Use the button element instead of <input type="button">') |
| |
| def DoNotUseSingleQuotesCheck(self, line_number, line): |
| regex = self.input_api.re.compile(""" |
| <\S+ # The tag name. |
| (?:\s+\S+\$?="[^"]*"|\s+\S+)* # Correctly quoted or non-value props. |
| \s+(\S+\$?='[^']*') # Find incorrectly quoted (foo='bar'). |
| [^>]*> # To the end of the tag. |
| """, |
| self.input_api.re.MULTILINE | self.input_api.re.VERBOSE) |
| return regex_check.RegexCheck(self.input_api.re, line_number, line, regex, |
| 'Use double quotes rather than single quotes in HTML properties') |
| |
| def I18nContentJavaScriptCaseCheck(self, line_number, line): |
| regex = self.input_api.re.compile(""" |
| (?:^|\s) # start of line or whitespace |
| i18n-content=" # i18n-content=" |
| ([A-Z][^"]*|[^"]*[-_][^"]*)" # starts with caps or contains '-' or '_' |
| """, |
| self.input_api.re.VERBOSE) |
| return regex_check.RegexCheck(self.input_api.re, line_number, line, regex, |
| "For i18n-content use javaScriptCase.") |
| |
| def LabelCheck(self, line_number, line): |
| regex = self.input_api.re.compile(""" |
| (?:^|\s) # start of line or whitespace |
| <label[^>]+? # <label tag |
| (for=) # for= |
| """, |
| self.input_api.re.VERBOSE) |
| return regex_check.RegexCheck(self.input_api.re, line_number, line, regex, |
| "Avoid 'for' attribute on <label>. Place the input within the <label>, " |
| "or use aria-labelledby for <select>.") |
| |
| def QuotePolymerBindings(self, line_number, line): |
| regex = self.input_api.re.compile(r"=(\[\[|\{\{)") |
| return regex_check.RegexCheck(self.input_api.re, line_number, line, regex, |
| 'Please use quotes around Polymer bindings (i.e. attr="[[prop]]")') |
| |
| def RunChecks(self): |
| """Check for violations of the Chromium web development style guide. See |
| https://chromium.googlesource.com/chromium/src/+/main/styleguide/web/web.md |
| """ |
| results = [] |
| |
| affected_files = self.input_api.AffectedFiles(file_filter=self.file_filter, |
| include_deletes=False) |
| |
| for f in affected_files: |
| if not f.LocalPath().endswith('.html'): |
| continue |
| |
| errors = [] |
| |
| for line_number, line in f.ChangedContents(): |
| errors.extend([ |
| _f for _f in [ |
| self.ClassesUseDashFormCheck(line_number, line), |
| self.DoNotCloseSingleTagsCheck(line_number, line), |
| self.DoNotUseBrElementCheck(line_number, line), |
| self.DoNotUseInputTypeButtonCheck(line_number, line), |
| self.I18nContentJavaScriptCaseCheck(line_number, line), |
| self.LabelCheck(line_number, line), |
| self.QuotePolymerBindings(line_number, line), |
| ] if _f |
| ]) |
| |
| if errors: |
| abs_local_path = f.AbsoluteLocalPath() |
| file_indicator = 'Found HTML style issues in %s' % abs_local_path |
| prompt_msg = file_indicator + '\n\n' + '\n'.join(errors) + '\n' |
| results.append(self.output_api.PresubmitPromptWarning(prompt_msg)) |
| |
| return results |