1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Checks to use in PRESUBMIT.py for HTML style violations.""" 6 7import collections 8import difflib 9import re 10 11import bs4 12 13from catapult_build import parse_html 14 15 16def RunChecks(input_api, output_api, excluded_paths=None): 17 18 def ShouldCheck(affected_file): 19 path = affected_file.LocalPath() 20 if not path.endswith('.html'): 21 return False 22 if not excluded_paths: 23 return True 24 return not any(re.match(pattern, path) for pattern in excluded_paths) 25 26 affected_files = input_api.AffectedFiles( 27 file_filter=ShouldCheck, include_deletes=False) 28 results = [] 29 for f in affected_files: 30 CheckAffectedFile(f, results, output_api) 31 return results 32 33 34def CheckAffectedFile(affected_file, results, output_api): 35 path = affected_file.LocalPath() 36 soup = parse_html.BeautifulSoup('\n'.join(affected_file.NewContents())) 37 for check in [CheckDoctype, CheckImportOrder]: 38 check(path, soup, results, output_api) 39 40 41def CheckDoctype(path, soup, results, output_api): 42 if _HasHtml5Declaration(soup): 43 return 44 error_text = 'Could not find "<!DOCTYPE html>" in %s.' % path 45 results.append(output_api.PresubmitError(error_text)) 46 47 48def _HasHtml5Declaration(soup): 49 for item in soup.contents: 50 if isinstance(item, bs4.Doctype) and item.lower() == 'html': 51 return True 52 return False 53 54 55def CheckImportOrder(path, soup, results, output_api): 56 grouped_hrefs = collections.defaultdict(list) # Link rel -> [link hrefs]. 57 for link in soup.find_all('link'): 58 grouped_hrefs[','.join(link.get('rel'))].append(link.get('href')) 59 60 for rel, actual_hrefs in grouped_hrefs.iteritems(): 61 expected_hrefs = list(sorted(set(actual_hrefs))) 62 if actual_hrefs != expected_hrefs: 63 error_text = ( 64 'Invalid "%s" link sort order in %s:\n' % (rel, path) + 65 ' ' + '\n '.join(difflib.ndiff(actual_hrefs, expected_hrefs))) 66 results.append(output_api.PresubmitError(error_text)) 67