Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / PRESUBMIT.py
1 # Copyright (c) 2012 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 """Top-level presubmit script for Chromium.
6
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8 for more details about the presubmit API built into gcl.
9 """
10
11
12 import re
13 import sys
14
15
16 _EXCLUDED_PATHS = (
17     r"^breakpad[\\\/].*",
18     r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
19     r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
20     r"^native_client_sdk[\\\/]src[\\\/]tools[\\\/].*.mk",
21     r"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
22     r"^skia[\\\/].*",
23     r"^v8[\\\/].*",
24     r".*MakeFile$",
25     r".+_autogen\.h$",
26     r".+[\\\/]pnacl_shim\.c$",
27     r"^gpu[\\\/]config[\\\/].*_list_json\.cc$",
28     r"^chrome[\\\/]browser[\\\/]resources[\\\/]pdf[\\\/]index.js"
29 )
30
31 # The NetscapePlugIn library is excluded from pan-project as it will soon
32 # be deleted together with the rest of the NPAPI and it's not worthwhile to
33 # update the coding style until then.
34 _TESTRUNNER_PATHS = (
35     r"^content[\\\/]shell[\\\/]tools[\\\/]plugin[\\\/].*",
36 )
37
38 # Fragment of a regular expression that matches C++ and Objective-C++
39 # implementation files.
40 _IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
41
42 # Regular expression that matches code only used for test binaries
43 # (best effort).
44 _TEST_CODE_EXCLUDED_PATHS = (
45     r'.*[\\\/](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
46     r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
47     r'.+_(api|browser|kif|perf|pixel|unit|ui)?test(_[a-z]+)?%s' %
48         _IMPLEMENTATION_EXTENSIONS,
49     r'.+profile_sync_service_harness%s' % _IMPLEMENTATION_EXTENSIONS,
50     r'.*[\\\/](test|tool(s)?)[\\\/].*',
51     # content_shell is used for running layout tests.
52     r'content[\\\/]shell[\\\/].*',
53     # At request of folks maintaining this folder.
54     r'chrome[\\\/]browser[\\\/]automation[\\\/].*',
55     # Non-production example code.
56     r'mojo[\\\/]examples[\\\/].*',
57     # Launcher for running iOS tests on the simulator.
58     r'testing[\\\/]iossim[\\\/]iossim\.mm$',
59 )
60
61 _TEST_ONLY_WARNING = (
62     'You might be calling functions intended only for testing from\n'
63     'production code.  It is OK to ignore this warning if you know what\n'
64     'you are doing, as the heuristics used to detect the situation are\n'
65     'not perfect.  The commit queue will not block on this warning.')
66
67
68 _INCLUDE_ORDER_WARNING = (
69     'Your #include order seems to be broken. Send mail to\n'
70     'marja@chromium.org if this is not the case.')
71
72
73 _BANNED_OBJC_FUNCTIONS = (
74     (
75       'addTrackingRect:',
76       (
77        'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
78        'prohibited. Please use CrTrackingArea instead.',
79        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
80       ),
81       False,
82     ),
83     (
84       r'/NSTrackingArea\W',
85       (
86        'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
87        'instead.',
88        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
89       ),
90       False,
91     ),
92     (
93       'convertPointFromBase:',
94       (
95        'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
96        'Please use |convertPoint:(point) fromView:nil| instead.',
97        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
98       ),
99       True,
100     ),
101     (
102       'convertPointToBase:',
103       (
104        'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
105        'Please use |convertPoint:(point) toView:nil| instead.',
106        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
107       ),
108       True,
109     ),
110     (
111       'convertRectFromBase:',
112       (
113        'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
114        'Please use |convertRect:(point) fromView:nil| instead.',
115        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
116       ),
117       True,
118     ),
119     (
120       'convertRectToBase:',
121       (
122        'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
123        'Please use |convertRect:(point) toView:nil| instead.',
124        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
125       ),
126       True,
127     ),
128     (
129       'convertSizeFromBase:',
130       (
131        'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
132        'Please use |convertSize:(point) fromView:nil| instead.',
133        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
134       ),
135       True,
136     ),
137     (
138       'convertSizeToBase:',
139       (
140        'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
141        'Please use |convertSize:(point) toView:nil| instead.',
142        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
143       ),
144       True,
145     ),
146 )
147
148
149 _BANNED_CPP_FUNCTIONS = (
150     # Make sure that gtest's FRIEND_TEST() macro is not used; the
151     # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
152     # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
153     (
154       'FRIEND_TEST(',
155       (
156        'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
157        'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
158       ),
159       False,
160       (),
161     ),
162     (
163       'ScopedAllowIO',
164       (
165        'New code should not use ScopedAllowIO. Post a task to the blocking',
166        'pool or the FILE thread instead.',
167       ),
168       True,
169       (
170         r"^base[\\\/]process[\\\/]process_metrics_linux\.cc$",
171         r"^chrome[\\\/]browser[\\\/]chromeos[\\\/]boot_times_loader\.cc$",
172         r"^components[\\\/]crash[\\\/]app[\\\/]breakpad_mac\.mm$",
173         r"^content[\\\/]shell[\\\/]browser[\\\/]shell_browser_main\.cc$",
174         r"^content[\\\/]shell[\\\/]browser[\\\/]shell_message_filter\.cc$",
175         r"^mojo[\\\/]system[\\\/]raw_shared_buffer_posix\.cc$",
176         r"^net[\\\/]disk_cache[\\\/]cache_util\.cc$",
177         r"^net[\\\/]url_request[\\\/]test_url_fetcher_factory\.cc$",
178       ),
179     ),
180     (
181       'SkRefPtr',
182       (
183         'The use of SkRefPtr is prohibited. ',
184         'Please use skia::RefPtr instead.'
185       ),
186       True,
187       (),
188     ),
189     (
190       'SkAutoRef',
191       (
192         'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
193         'Please use skia::RefPtr instead.'
194       ),
195       True,
196       (),
197     ),
198     (
199       'SkAutoTUnref',
200       (
201         'The use of SkAutoTUnref is dangerous because it implicitly ',
202         'converts to a raw pointer. Please use skia::RefPtr instead.'
203       ),
204       True,
205       (),
206     ),
207     (
208       'SkAutoUnref',
209       (
210         'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
211         'because it implicitly converts to a raw pointer. ',
212         'Please use skia::RefPtr instead.'
213       ),
214       True,
215       (),
216     ),
217     (
218       r'/HANDLE_EINTR\(.*close',
219       (
220        'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
221        'descriptor will be closed, and it is incorrect to retry the close.',
222        'Either call close directly and ignore its return value, or wrap close',
223        'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
224       ),
225       True,
226       (),
227     ),
228     (
229       r'/IGNORE_EINTR\((?!.*close)',
230       (
231        'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
232        'calls, use HANDLE_EINTR. See http://crbug.com/269623',
233       ),
234       True,
235       (
236         # Files that #define IGNORE_EINTR.
237         r'^base[\\\/]posix[\\\/]eintr_wrapper\.h$',
238         r'^ppapi[\\\/]tests[\\\/]test_broker\.cc$',
239       ),
240     ),
241     (
242       r'/v8::Extension\(',
243       (
244         'Do not introduce new v8::Extensions into the code base, use',
245         'gin::Wrappable instead. See http://crbug.com/334679',
246       ),
247       True,
248       (
249         r'extensions[\\\/]renderer[\\\/]safe_builtins\.*',
250       ),
251     ),
252 )
253
254 _IPC_ENUM_TRAITS_DEPRECATED = (
255     'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
256     'See http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc')
257
258
259 _VALID_OS_MACROS = (
260     # Please keep sorted.
261     'OS_ANDROID',
262     'OS_ANDROID_HOST',
263     'OS_BSD',
264     'OS_CAT',       # For testing.
265     'OS_CHROMEOS',
266     'OS_FREEBSD',
267     'OS_IOS',
268     'OS_LINUX',
269     'OS_MACOSX',
270     'OS_NACL',
271     'OS_OPENBSD',
272     'OS_POSIX',
273     'OS_QNX',
274     'OS_SOLARIS',
275     'OS_WIN',
276 )
277
278
279 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
280   """Attempts to prevent use of functions intended only for testing in
281   non-testing code. For now this is just a best-effort implementation
282   that ignores header files and may have some false positives. A
283   better implementation would probably need a proper C++ parser.
284   """
285   # We only scan .cc files and the like, as the declaration of
286   # for-testing functions in header files are hard to distinguish from
287   # calls to such functions without a proper C++ parser.
288   file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
289
290   base_function_pattern = r'[ :]test::[^\s]+|ForTest(ing)?|for_test(ing)?'
291   inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
292   comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
293   exclusion_pattern = input_api.re.compile(
294     r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
295       base_function_pattern, base_function_pattern))
296
297   def FilterFile(affected_file):
298     black_list = (_EXCLUDED_PATHS +
299                   _TEST_CODE_EXCLUDED_PATHS +
300                   input_api.DEFAULT_BLACK_LIST)
301     return input_api.FilterSourceFile(
302       affected_file,
303       white_list=(file_inclusion_pattern, ),
304       black_list=black_list)
305
306   problems = []
307   for f in input_api.AffectedSourceFiles(FilterFile):
308     local_path = f.LocalPath()
309     for line_number, line in f.ChangedContents():
310       if (inclusion_pattern.search(line) and
311           not comment_pattern.search(line) and
312           not exclusion_pattern.search(line)):
313         problems.append(
314           '%s:%d\n    %s' % (local_path, line_number, line.strip()))
315
316   if problems:
317     return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
318   else:
319     return []
320
321
322 def _CheckNoIOStreamInHeaders(input_api, output_api):
323   """Checks to make sure no .h files include <iostream>."""
324   files = []
325   pattern = input_api.re.compile(r'^#include\s*<iostream>',
326                                  input_api.re.MULTILINE)
327   for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
328     if not f.LocalPath().endswith('.h'):
329       continue
330     contents = input_api.ReadFile(f)
331     if pattern.search(contents):
332       files.append(f)
333
334   if len(files):
335     return [ output_api.PresubmitError(
336         'Do not #include <iostream> in header files, since it inserts static '
337         'initialization into every file including the header. Instead, '
338         '#include <ostream>. See http://crbug.com/94794',
339         files) ]
340   return []
341
342
343 def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
344   """Checks to make sure no source files use UNIT_TEST"""
345   problems = []
346   for f in input_api.AffectedFiles():
347     if (not f.LocalPath().endswith(('.cc', '.mm'))):
348       continue
349
350     for line_num, line in f.ChangedContents():
351       if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
352         problems.append('    %s:%d' % (f.LocalPath(), line_num))
353
354   if not problems:
355     return []
356   return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
357       '\n'.join(problems))]
358
359
360 def _CheckNoNewWStrings(input_api, output_api):
361   """Checks to make sure we don't introduce use of wstrings."""
362   problems = []
363   for f in input_api.AffectedFiles():
364     if (not f.LocalPath().endswith(('.cc', '.h')) or
365         f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h'))):
366       continue
367
368     allowWString = False
369     for line_num, line in f.ChangedContents():
370       if 'presubmit: allow wstring' in line:
371         allowWString = True
372       elif not allowWString and 'wstring' in line:
373         problems.append('    %s:%d' % (f.LocalPath(), line_num))
374         allowWString = False
375       else:
376         allowWString = False
377
378   if not problems:
379     return []
380   return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
381       '  If you are calling a cross-platform API that accepts a wstring, '
382       'fix the API.\n' +
383       '\n'.join(problems))]
384
385
386 def _CheckNoDEPSGIT(input_api, output_api):
387   """Make sure .DEPS.git is never modified manually."""
388   if any(f.LocalPath().endswith('.DEPS.git') for f in
389       input_api.AffectedFiles()):
390     return [output_api.PresubmitError(
391       'Never commit changes to .DEPS.git. This file is maintained by an\n'
392       'automated system based on what\'s in DEPS and your changes will be\n'
393       'overwritten.\n'
394       'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/get-the-code#Rolling_DEPS\n'
395       'for more information')]
396   return []
397
398
399 def _CheckValidHostsInDEPS(input_api, output_api):
400   """Checks that DEPS file deps are from allowed_hosts."""
401   # Run only if DEPS file has been modified to annoy fewer bystanders.
402   if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
403     return []
404   # Outsource work to gclient verify
405   try:
406     input_api.subprocess.check_output(['gclient', 'verify'])
407     return []
408   except input_api.subprocess.CalledProcessError, error:
409     return [output_api.PresubmitError(
410         'DEPS file must have only git dependencies.',
411         long_text=error.output)]
412
413
414 def _CheckNoBannedFunctions(input_api, output_api):
415   """Make sure that banned functions are not used."""
416   warnings = []
417   errors = []
418
419   file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
420   for f in input_api.AffectedFiles(file_filter=file_filter):
421     for line_num, line in f.ChangedContents():
422       for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
423         matched = False
424         if func_name[0:1] == '/':
425           regex = func_name[1:]
426           if input_api.re.search(regex, line):
427             matched = True
428         elif func_name in line:
429             matched = True
430         if matched:
431           problems = warnings;
432           if error:
433             problems = errors;
434           problems.append('    %s:%d:' % (f.LocalPath(), line_num))
435           for message_line in message:
436             problems.append('      %s' % message_line)
437
438   file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
439   for f in input_api.AffectedFiles(file_filter=file_filter):
440     for line_num, line in f.ChangedContents():
441       for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
442         def IsBlacklisted(affected_file, blacklist):
443           local_path = affected_file.LocalPath()
444           for item in blacklist:
445             if input_api.re.match(item, local_path):
446               return True
447           return False
448         if IsBlacklisted(f, excluded_paths):
449           continue
450         matched = False
451         if func_name[0:1] == '/':
452           regex = func_name[1:]
453           if input_api.re.search(regex, line):
454             matched = True
455         elif func_name in line:
456             matched = True
457         if matched:
458           problems = warnings;
459           if error:
460             problems = errors;
461           problems.append('    %s:%d:' % (f.LocalPath(), line_num))
462           for message_line in message:
463             problems.append('      %s' % message_line)
464
465   result = []
466   if (warnings):
467     result.append(output_api.PresubmitPromptWarning(
468         'Banned functions were used.\n' + '\n'.join(warnings)))
469   if (errors):
470     result.append(output_api.PresubmitError(
471         'Banned functions were used.\n' + '\n'.join(errors)))
472   return result
473
474
475 def _CheckNoPragmaOnce(input_api, output_api):
476   """Make sure that banned functions are not used."""
477   files = []
478   pattern = input_api.re.compile(r'^#pragma\s+once',
479                                  input_api.re.MULTILINE)
480   for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
481     if not f.LocalPath().endswith('.h'):
482       continue
483     contents = input_api.ReadFile(f)
484     if pattern.search(contents):
485       files.append(f)
486
487   if files:
488     return [output_api.PresubmitError(
489         'Do not use #pragma once in header files.\n'
490         'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
491         files)]
492   return []
493
494
495 def _CheckNoTrinaryTrueFalse(input_api, output_api):
496   """Checks to make sure we don't introduce use of foo ? true : false."""
497   problems = []
498   pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
499   for f in input_api.AffectedFiles():
500     if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
501       continue
502
503     for line_num, line in f.ChangedContents():
504       if pattern.match(line):
505         problems.append('    %s:%d' % (f.LocalPath(), line_num))
506
507   if not problems:
508     return []
509   return [output_api.PresubmitPromptWarning(
510       'Please consider avoiding the "? true : false" pattern if possible.\n' +
511       '\n'.join(problems))]
512
513
514 def _CheckUnwantedDependencies(input_api, output_api):
515   """Runs checkdeps on #include statements added in this
516   change. Breaking - rules is an error, breaking ! rules is a
517   warning.
518   """
519   # We need to wait until we have an input_api object and use this
520   # roundabout construct to import checkdeps because this file is
521   # eval-ed and thus doesn't have __file__.
522   original_sys_path = sys.path
523   try:
524     sys.path = sys.path + [input_api.os_path.join(
525         input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
526     import checkdeps
527     from cpp_checker import CppChecker
528     from rules import Rule
529   finally:
530     # Restore sys.path to what it was before.
531     sys.path = original_sys_path
532
533   added_includes = []
534   for f in input_api.AffectedFiles():
535     if not CppChecker.IsCppFile(f.LocalPath()):
536       continue
537
538     changed_lines = [line for line_num, line in f.ChangedContents()]
539     added_includes.append([f.LocalPath(), changed_lines])
540
541   deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
542
543   error_descriptions = []
544   warning_descriptions = []
545   for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
546       added_includes):
547     description_with_path = '%s\n    %s' % (path, rule_description)
548     if rule_type == Rule.DISALLOW:
549       error_descriptions.append(description_with_path)
550     else:
551       warning_descriptions.append(description_with_path)
552
553   results = []
554   if error_descriptions:
555     results.append(output_api.PresubmitError(
556         'You added one or more #includes that violate checkdeps rules.',
557         error_descriptions))
558   if warning_descriptions:
559     results.append(output_api.PresubmitPromptOrNotify(
560         'You added one or more #includes of files that are temporarily\n'
561         'allowed but being removed. Can you avoid introducing the\n'
562         '#include? See relevant DEPS file(s) for details and contacts.',
563         warning_descriptions))
564   return results
565
566
567 def _CheckFilePermissions(input_api, output_api):
568   """Check that all files have their permissions properly set."""
569   if input_api.platform == 'win32':
570     return []
571   args = [sys.executable, 'tools/checkperms/checkperms.py', '--root',
572           input_api.change.RepositoryRoot()]
573   for f in input_api.AffectedFiles():
574     args += ['--file', f.LocalPath()]
575   checkperms = input_api.subprocess.Popen(args,
576                                           stdout=input_api.subprocess.PIPE)
577   errors = checkperms.communicate()[0].strip()
578   if errors:
579     return [output_api.PresubmitError('checkperms.py failed.',
580                                       errors.splitlines())]
581   return []
582
583
584 def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
585   """Makes sure we don't include ui/aura/window_property.h
586   in header files.
587   """
588   pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
589   errors = []
590   for f in input_api.AffectedFiles():
591     if not f.LocalPath().endswith('.h'):
592       continue
593     for line_num, line in f.ChangedContents():
594       if pattern.match(line):
595         errors.append('    %s:%d' % (f.LocalPath(), line_num))
596
597   results = []
598   if errors:
599     results.append(output_api.PresubmitError(
600       'Header files should not include ui/aura/window_property.h', errors))
601   return results
602
603
604 def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
605   """Checks that the lines in scope occur in the right order.
606
607   1. C system files in alphabetical order
608   2. C++ system files in alphabetical order
609   3. Project's .h files
610   """
611
612   c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
613   cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
614   custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
615
616   C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
617
618   state = C_SYSTEM_INCLUDES
619
620   previous_line = ''
621   previous_line_num = 0
622   problem_linenums = []
623   for line_num, line in scope:
624     if c_system_include_pattern.match(line):
625       if state != C_SYSTEM_INCLUDES:
626         problem_linenums.append((line_num, previous_line_num))
627       elif previous_line and previous_line > line:
628         problem_linenums.append((line_num, previous_line_num))
629     elif cpp_system_include_pattern.match(line):
630       if state == C_SYSTEM_INCLUDES:
631         state = CPP_SYSTEM_INCLUDES
632       elif state == CUSTOM_INCLUDES:
633         problem_linenums.append((line_num, previous_line_num))
634       elif previous_line and previous_line > line:
635         problem_linenums.append((line_num, previous_line_num))
636     elif custom_include_pattern.match(line):
637       if state != CUSTOM_INCLUDES:
638         state = CUSTOM_INCLUDES
639       elif previous_line and previous_line > line:
640         problem_linenums.append((line_num, previous_line_num))
641     else:
642       problem_linenums.append(line_num)
643     previous_line = line
644     previous_line_num = line_num
645
646   warnings = []
647   for (line_num, previous_line_num) in problem_linenums:
648     if line_num in changed_linenums or previous_line_num in changed_linenums:
649       warnings.append('    %s:%d' % (file_path, line_num))
650   return warnings
651
652
653 def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
654   """Checks the #include order for the given file f."""
655
656   system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
657   # Exclude the following includes from the check:
658   # 1) #include <.../...>, e.g., <sys/...> includes often need to appear in a
659   # specific order.
660   # 2) <atlbase.h>, "build/build_config.h"
661   excluded_include_pattern = input_api.re.compile(
662       r'\s*#include (\<.*/.*|\<atlbase\.h\>|"build/build_config.h")')
663   custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
664   # Match the final or penultimate token if it is xxxtest so we can ignore it
665   # when considering the special first include.
666   test_file_tag_pattern = input_api.re.compile(
667     r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
668   if_pattern = input_api.re.compile(
669       r'\s*#\s*(if|elif|else|endif|define|undef).*')
670   # Some files need specialized order of includes; exclude such files from this
671   # check.
672   uncheckable_includes_pattern = input_api.re.compile(
673       r'\s*#include '
674       '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
675
676   contents = f.NewContents()
677   warnings = []
678   line_num = 0
679
680   # Handle the special first include. If the first include file is
681   # some/path/file.h, the corresponding including file can be some/path/file.cc,
682   # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
683   # etc. It's also possible that no special first include exists.
684   # If the included file is some/path/file_platform.h the including file could
685   # also be some/path/file_xxxtest_platform.h.
686   including_file_base_name = test_file_tag_pattern.sub(
687     '', input_api.os_path.basename(f.LocalPath()))
688
689   for line in contents:
690     line_num += 1
691     if system_include_pattern.match(line):
692       # No special first include -> process the line again along with normal
693       # includes.
694       line_num -= 1
695       break
696     match = custom_include_pattern.match(line)
697     if match:
698       match_dict = match.groupdict()
699       header_basename = test_file_tag_pattern.sub(
700         '', input_api.os_path.basename(match_dict['FILE'])).replace('.h', '')
701
702       if header_basename not in including_file_base_name:
703         # No special first include -> process the line again along with normal
704         # includes.
705         line_num -= 1
706       break
707
708   # Split into scopes: Each region between #if and #endif is its own scope.
709   scopes = []
710   current_scope = []
711   for line in contents[line_num:]:
712     line_num += 1
713     if uncheckable_includes_pattern.match(line):
714       continue
715     if if_pattern.match(line):
716       scopes.append(current_scope)
717       current_scope = []
718     elif ((system_include_pattern.match(line) or
719            custom_include_pattern.match(line)) and
720           not excluded_include_pattern.match(line)):
721       current_scope.append((line_num, line))
722   scopes.append(current_scope)
723
724   for scope in scopes:
725     warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
726                                                changed_linenums))
727   return warnings
728
729
730 def _CheckIncludeOrder(input_api, output_api):
731   """Checks that the #include order is correct.
732
733   1. The corresponding header for source files.
734   2. C system files in alphabetical order
735   3. C++ system files in alphabetical order
736   4. Project's .h files in alphabetical order
737
738   Each region separated by #if, #elif, #else, #endif, #define and #undef follows
739   these rules separately.
740   """
741   def FileFilterIncludeOrder(affected_file):
742     black_list = (_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
743     return input_api.FilterSourceFile(affected_file, black_list=black_list)
744
745   warnings = []
746   for f in input_api.AffectedFiles(file_filter=FileFilterIncludeOrder):
747     if f.LocalPath().endswith(('.cc', '.h')):
748       changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
749       warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
750
751   results = []
752   if warnings:
753     results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
754                                                       warnings))
755   return results
756
757
758 def _CheckForVersionControlConflictsInFile(input_api, f):
759   pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
760   errors = []
761   for line_num, line in f.ChangedContents():
762     if pattern.match(line):
763       errors.append('    %s:%d %s' % (f.LocalPath(), line_num, line))
764   return errors
765
766
767 def _CheckForVersionControlConflicts(input_api, output_api):
768   """Usually this is not intentional and will cause a compile failure."""
769   errors = []
770   for f in input_api.AffectedFiles():
771     errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
772
773   results = []
774   if errors:
775     results.append(output_api.PresubmitError(
776       'Version control conflict markers found, please resolve.', errors))
777   return results
778
779
780 def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
781   def FilterFile(affected_file):
782     """Filter function for use with input_api.AffectedSourceFiles,
783     below.  This filters out everything except non-test files from
784     top-level directories that generally speaking should not hard-code
785     service URLs (e.g. src/android_webview/, src/content/ and others).
786     """
787     return input_api.FilterSourceFile(
788       affected_file,
789       white_list=(r'^(android_webview|base|content|net)[\\\/].*', ),
790       black_list=(_EXCLUDED_PATHS +
791                   _TEST_CODE_EXCLUDED_PATHS +
792                   input_api.DEFAULT_BLACK_LIST))
793
794   base_pattern = '"[^"]*google\.com[^"]*"'
795   comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
796   pattern = input_api.re.compile(base_pattern)
797   problems = []  # items are (filename, line_number, line)
798   for f in input_api.AffectedSourceFiles(FilterFile):
799     for line_num, line in f.ChangedContents():
800       if not comment_pattern.search(line) and pattern.search(line):
801         problems.append((f.LocalPath(), line_num, line))
802
803   if problems:
804     return [output_api.PresubmitPromptOrNotify(
805         'Most layers below src/chrome/ should not hardcode service URLs.\n'
806         'Are you sure this is correct?',
807         ['  %s:%d:  %s' % (
808             problem[0], problem[1], problem[2]) for problem in problems])]
809   else:
810     return []
811
812
813 def _CheckNoAbbreviationInPngFileName(input_api, output_api):
814   """Makes sure there are no abbreviations in the name of PNG files.
815   """
816   pattern = input_api.re.compile(r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$')
817   errors = []
818   for f in input_api.AffectedFiles(include_deletes=False):
819     if pattern.match(f.LocalPath()):
820       errors.append('    %s' % f.LocalPath())
821
822   results = []
823   if errors:
824     results.append(output_api.PresubmitError(
825         'The name of PNG files should not have abbreviations. \n'
826         'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
827         'Contact oshima@chromium.org if you have questions.', errors))
828   return results
829
830
831 def _FilesToCheckForIncomingDeps(re, changed_lines):
832   """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
833   a set of DEPS entries that we should look up.
834
835   For a directory (rather than a specific filename) we fake a path to
836   a specific filename by adding /DEPS. This is chosen as a file that
837   will seldom or never be subject to per-file include_rules.
838   """
839   # We ignore deps entries on auto-generated directories.
840   AUTO_GENERATED_DIRS = ['grit', 'jni']
841
842   # This pattern grabs the path without basename in the first
843   # parentheses, and the basename (if present) in the second. It
844   # relies on the simple heuristic that if there is a basename it will
845   # be a header file ending in ".h".
846   pattern = re.compile(
847       r"""['"]\+([^'"]+?)(/[a-zA-Z0-9_]+\.h)?['"].*""")
848   results = set()
849   for changed_line in changed_lines:
850     m = pattern.match(changed_line)
851     if m:
852       path = m.group(1)
853       if path.split('/')[0] not in AUTO_GENERATED_DIRS:
854         if m.group(2):
855           results.add('%s%s' % (path, m.group(2)))
856         else:
857           results.add('%s/DEPS' % path)
858   return results
859
860
861 def _CheckAddedDepsHaveTargetApprovals(input_api, output_api):
862   """When a dependency prefixed with + is added to a DEPS file, we
863   want to make sure that the change is reviewed by an OWNER of the
864   target file or directory, to avoid layering violations from being
865   introduced. This check verifies that this happens.
866   """
867   changed_lines = set()
868   for f in input_api.AffectedFiles():
869     filename = input_api.os_path.basename(f.LocalPath())
870     if filename == 'DEPS':
871       changed_lines |= set(line.strip()
872                            for line_num, line
873                            in f.ChangedContents())
874   if not changed_lines:
875     return []
876
877   virtual_depended_on_files = _FilesToCheckForIncomingDeps(input_api.re,
878                                                            changed_lines)
879   if not virtual_depended_on_files:
880     return []
881
882   if input_api.is_committing:
883     if input_api.tbr:
884       return [output_api.PresubmitNotifyResult(
885           '--tbr was specified, skipping OWNERS check for DEPS additions')]
886     if not input_api.change.issue:
887       return [output_api.PresubmitError(
888           "DEPS approval by OWNERS check failed: this change has "
889           "no Rietveld issue number, so we can't check it for approvals.")]
890     output = output_api.PresubmitError
891   else:
892     output = output_api.PresubmitNotifyResult
893
894   owners_db = input_api.owners_db
895   owner_email, reviewers = input_api.canned_checks._RietveldOwnerAndReviewers(
896       input_api,
897       owners_db.email_regexp,
898       approval_needed=input_api.is_committing)
899
900   owner_email = owner_email or input_api.change.author_email
901
902   reviewers_plus_owner = set(reviewers)
903   if owner_email:
904     reviewers_plus_owner.add(owner_email)
905   missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
906                                                  reviewers_plus_owner)
907
908   # We strip the /DEPS part that was added by
909   # _FilesToCheckForIncomingDeps to fake a path to a file in a
910   # directory.
911   def StripDeps(path):
912     start_deps = path.rfind('/DEPS')
913     if start_deps != -1:
914       return path[:start_deps]
915     else:
916       return path
917   unapproved_dependencies = ["'+%s'," % StripDeps(path)
918                              for path in missing_files]
919
920   if unapproved_dependencies:
921     output_list = [
922       output('Missing LGTM from OWNERS of dependencies added to DEPS:\n    %s' %
923              '\n    '.join(sorted(unapproved_dependencies)))]
924     if not input_api.is_committing:
925       suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
926       output_list.append(output(
927           'Suggested missing target path OWNERS:\n    %s' %
928           '\n    '.join(suggested_owners or [])))
929     return output_list
930
931   return []
932
933
934 def _CheckSpamLogging(input_api, output_api):
935   file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
936   black_list = (_EXCLUDED_PATHS +
937                 _TEST_CODE_EXCLUDED_PATHS +
938                 input_api.DEFAULT_BLACK_LIST +
939                 (r"^base[\\\/]logging\.h$",
940                  r"^base[\\\/]logging\.cc$",
941                  r"^chrome[\\\/]app[\\\/]chrome_main_delegate\.cc$",
942                  r"^chrome[\\\/]browser[\\\/]chrome_browser_main\.cc$",
943                  r"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]"
944                      r"startup_browser_creator\.cc$",
945                  r"^chrome[\\\/]installer[\\\/]setup[\\\/].*",
946                  r"chrome[\\\/]browser[\\\/]diagnostics[\\\/]" +
947                      r"diagnostics_writer\.cc$",
948                  r"^chrome_elf[\\\/]dll_hash[\\\/]dll_hash_main\.cc$",
949                  r"^chromecast[\\\/]",
950                  r"^cloud_print[\\\/]",
951                  r"^content[\\\/]common[\\\/]gpu[\\\/]client[\\\/]"
952                      r"gl_helper_benchmark\.cc$",
953                  r"^courgette[\\\/]courgette_tool\.cc$",
954                  r"^extensions[\\\/]renderer[\\\/]logging_native_handler\.cc$",
955                  r"^native_client_sdk[\\\/]",
956                  r"^remoting[\\\/]base[\\\/]logging\.h$",
957                  r"^remoting[\\\/]host[\\\/].*",
958                  r"^sandbox[\\\/]linux[\\\/].*",
959                  r"^tools[\\\/]",
960                  r"^ui[\\\/]aura[\\\/]bench[\\\/]bench_main\.cc$",
961                  r"^webkit[\\\/]browser[\\\/]fileapi[\\\/]" +
962                      r"dump_file_system.cc$",))
963   source_file_filter = lambda x: input_api.FilterSourceFile(
964       x, white_list=(file_inclusion_pattern,), black_list=black_list)
965
966   log_info = []
967   printf = []
968
969   for f in input_api.AffectedSourceFiles(source_file_filter):
970     contents = input_api.ReadFile(f, 'rb')
971     if re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", contents):
972       log_info.append(f.LocalPath())
973     elif re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", contents):
974       log_info.append(f.LocalPath())
975
976     if re.search(r"\bprintf\(", contents):
977       printf.append(f.LocalPath())
978     elif re.search(r"\bfprintf\((stdout|stderr)", contents):
979       printf.append(f.LocalPath())
980
981   if log_info:
982     return [output_api.PresubmitError(
983       'These files spam the console log with LOG(INFO):',
984       items=log_info)]
985   if printf:
986     return [output_api.PresubmitError(
987       'These files spam the console log with printf/fprintf:',
988       items=printf)]
989   return []
990
991
992 def _CheckForAnonymousVariables(input_api, output_api):
993   """These types are all expected to hold locks while in scope and
994      so should never be anonymous (which causes them to be immediately
995      destroyed)."""
996   they_who_must_be_named = [
997     'base::AutoLock',
998     'base::AutoReset',
999     'base::AutoUnlock',
1000     'SkAutoAlphaRestore',
1001     'SkAutoBitmapShaderInstall',
1002     'SkAutoBlitterChoose',
1003     'SkAutoBounderCommit',
1004     'SkAutoCallProc',
1005     'SkAutoCanvasRestore',
1006     'SkAutoCommentBlock',
1007     'SkAutoDescriptor',
1008     'SkAutoDisableDirectionCheck',
1009     'SkAutoDisableOvalCheck',
1010     'SkAutoFree',
1011     'SkAutoGlyphCache',
1012     'SkAutoHDC',
1013     'SkAutoLockColors',
1014     'SkAutoLockPixels',
1015     'SkAutoMalloc',
1016     'SkAutoMaskFreeImage',
1017     'SkAutoMutexAcquire',
1018     'SkAutoPathBoundsUpdate',
1019     'SkAutoPDFRelease',
1020     'SkAutoRasterClipValidate',
1021     'SkAutoRef',
1022     'SkAutoTime',
1023     'SkAutoTrace',
1024     'SkAutoUnref',
1025   ]
1026   anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
1027   # bad: base::AutoLock(lock.get());
1028   # not bad: base::AutoLock lock(lock.get());
1029   bad_pattern = input_api.re.compile(anonymous)
1030   # good: new base::AutoLock(lock.get())
1031   good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
1032   errors = []
1033
1034   for f in input_api.AffectedFiles():
1035     if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1036       continue
1037     for linenum, line in f.ChangedContents():
1038       if bad_pattern.search(line) and not good_pattern.search(line):
1039         errors.append('%s:%d' % (f.LocalPath(), linenum))
1040
1041   if errors:
1042     return [output_api.PresubmitError(
1043       'These lines create anonymous variables that need to be named:',
1044       items=errors)]
1045   return []
1046
1047
1048 def _CheckCygwinShell(input_api, output_api):
1049   source_file_filter = lambda x: input_api.FilterSourceFile(
1050       x, white_list=(r'.+\.(gyp|gypi)$',))
1051   cygwin_shell = []
1052
1053   for f in input_api.AffectedSourceFiles(source_file_filter):
1054     for linenum, line in f.ChangedContents():
1055       if 'msvs_cygwin_shell' in line:
1056         cygwin_shell.append(f.LocalPath())
1057         break
1058
1059   if cygwin_shell:
1060     return [output_api.PresubmitError(
1061       'These files should not use msvs_cygwin_shell (the default is 0):',
1062       items=cygwin_shell)]
1063   return []
1064
1065
1066 def _CheckUserActionUpdate(input_api, output_api):
1067   """Checks if any new user action has been added."""
1068   if any('actions.xml' == input_api.os_path.basename(f) for f in
1069          input_api.LocalPaths()):
1070     # If actions.xml is already included in the changelist, the PRESUBMIT
1071     # for actions.xml will do a more complete presubmit check.
1072     return []
1073
1074   file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm'))
1075   action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
1076   current_actions = None
1077   for f in input_api.AffectedFiles(file_filter=file_filter):
1078     for line_num, line in f.ChangedContents():
1079       match = input_api.re.search(action_re, line)
1080       if match:
1081         # Loads contents in tools/metrics/actions/actions.xml to memory. It's
1082         # loaded only once.
1083         if not current_actions:
1084           with open('tools/metrics/actions/actions.xml') as actions_f:
1085             current_actions = actions_f.read()
1086         # Search for the matched user action name in |current_actions|.
1087         for action_name in match.groups():
1088           action = 'name="{0}"'.format(action_name)
1089           if action not in current_actions:
1090             return [output_api.PresubmitPromptWarning(
1091               'File %s line %d: %s is missing in '
1092               'tools/metrics/actions/actions.xml. Please run '
1093               'tools/metrics/actions/extract_actions.py to update.'
1094               % (f.LocalPath(), line_num, action_name))]
1095   return []
1096
1097
1098 def _GetJSONParseError(input_api, filename, eat_comments=True):
1099   try:
1100     contents = input_api.ReadFile(filename)
1101     if eat_comments:
1102       json_comment_eater = input_api.os_path.join(
1103           input_api.PresubmitLocalPath(),
1104           'tools', 'json_comment_eater', 'json_comment_eater.py')
1105       process = input_api.subprocess.Popen(
1106           [input_api.python_executable, json_comment_eater],
1107           stdin=input_api.subprocess.PIPE,
1108           stdout=input_api.subprocess.PIPE,
1109           universal_newlines=True)
1110       (contents, _) = process.communicate(input=contents)
1111
1112     input_api.json.loads(contents)
1113   except ValueError as e:
1114     return e
1115   return None
1116
1117
1118 def _GetIDLParseError(input_api, filename):
1119   try:
1120     contents = input_api.ReadFile(filename)
1121     idl_schema = input_api.os_path.join(
1122         input_api.PresubmitLocalPath(),
1123         'tools', 'json_schema_compiler', 'idl_schema.py')
1124     process = input_api.subprocess.Popen(
1125         [input_api.python_executable, idl_schema],
1126         stdin=input_api.subprocess.PIPE,
1127         stdout=input_api.subprocess.PIPE,
1128         stderr=input_api.subprocess.PIPE,
1129         universal_newlines=True)
1130     (_, error) = process.communicate(input=contents)
1131     return error or None
1132   except ValueError as e:
1133     return e
1134
1135
1136 def _CheckParseErrors(input_api, output_api):
1137   """Check that IDL and JSON files do not contain syntax errors."""
1138   actions = {
1139     '.idl': _GetIDLParseError,
1140     '.json': _GetJSONParseError,
1141   }
1142   # These paths contain test data and other known invalid JSON files.
1143   excluded_patterns = [
1144     r'test[\\\/]data[\\\/]',
1145     r'^components[\\\/]policy[\\\/]resources[\\\/]policy_templates\.json$',
1146   ]
1147   # Most JSON files are preprocessed and support comments, but these do not.
1148   json_no_comments_patterns = [
1149     r'^testing[\\\/]',
1150   ]
1151   # Only run IDL checker on files in these directories.
1152   idl_included_patterns = [
1153     r'^chrome[\\\/]common[\\\/]extensions[\\\/]api[\\\/]',
1154     r'^extensions[\\\/]common[\\\/]api[\\\/]',
1155   ]
1156
1157   def get_action(affected_file):
1158     filename = affected_file.LocalPath()
1159     return actions.get(input_api.os_path.splitext(filename)[1])
1160
1161   def MatchesFile(patterns, path):
1162     for pattern in patterns:
1163       if input_api.re.search(pattern, path):
1164         return True
1165     return False
1166
1167   def FilterFile(affected_file):
1168     action = get_action(affected_file)
1169     if not action:
1170       return False
1171     path = affected_file.LocalPath()
1172
1173     if MatchesFile(excluded_patterns, path):
1174       return False
1175
1176     if (action == _GetIDLParseError and
1177         not MatchesFile(idl_included_patterns, path)):
1178       return False
1179     return True
1180
1181   results = []
1182   for affected_file in input_api.AffectedFiles(
1183       file_filter=FilterFile, include_deletes=False):
1184     action = get_action(affected_file)
1185     kwargs = {}
1186     if (action == _GetJSONParseError and
1187         MatchesFile(json_no_comments_patterns, affected_file.LocalPath())):
1188       kwargs['eat_comments'] = False
1189     parse_error = action(input_api,
1190                          affected_file.AbsoluteLocalPath(),
1191                          **kwargs)
1192     if parse_error:
1193       results.append(output_api.PresubmitError('%s could not be parsed: %s' %
1194           (affected_file.LocalPath(), parse_error)))
1195   return results
1196
1197
1198 def _CheckJavaStyle(input_api, output_api):
1199   """Runs checkstyle on changed java files and returns errors if any exist."""
1200   original_sys_path = sys.path
1201   try:
1202     sys.path = sys.path + [input_api.os_path.join(
1203         input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1204     import checkstyle
1205   finally:
1206     # Restore sys.path to what it was before.
1207     sys.path = original_sys_path
1208
1209   return checkstyle.RunCheckstyle(
1210       input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml')
1211
1212
1213 _DEPRECATED_CSS = [
1214   # Values
1215   ( "-webkit-box", "flex" ),
1216   ( "-webkit-inline-box", "inline-flex" ),
1217   ( "-webkit-flex", "flex" ),
1218   ( "-webkit-inline-flex", "inline-flex" ),
1219   ( "-webkit-min-content", "min-content" ),
1220   ( "-webkit-max-content", "max-content" ),
1221
1222   # Properties
1223   ( "-webkit-background-clip", "background-clip" ),
1224   ( "-webkit-background-origin", "background-origin" ),
1225   ( "-webkit-background-size", "background-size" ),
1226   ( "-webkit-box-shadow", "box-shadow" ),
1227
1228   # Functions
1229   ( "-webkit-gradient", "gradient" ),
1230   ( "-webkit-repeating-gradient", "repeating-gradient" ),
1231   ( "-webkit-linear-gradient", "linear-gradient" ),
1232   ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
1233   ( "-webkit-radial-gradient", "radial-gradient" ),
1234   ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
1235 ]
1236
1237 def _CheckNoDeprecatedCSS(input_api, output_api):
1238   """ Make sure that we don't use deprecated CSS
1239       properties, functions or values. Our external
1240       documentation is ignored by the hooks as it
1241       needs to be consumed by WebKit. """
1242   results = []
1243   file_inclusion_pattern = (r".+\.css$")
1244   black_list = (_EXCLUDED_PATHS +
1245                 _TEST_CODE_EXCLUDED_PATHS +
1246                 input_api.DEFAULT_BLACK_LIST +
1247                 (r"^chrome/common/extensions/docs",
1248                  r"^chrome/docs",
1249                  r"^native_client_sdk"))
1250   file_filter = lambda f: input_api.FilterSourceFile(
1251       f, white_list=file_inclusion_pattern, black_list=black_list)
1252   for fpath in input_api.AffectedFiles(file_filter=file_filter):
1253     for line_num, line in fpath.ChangedContents():
1254       for (deprecated_value, value) in _DEPRECATED_CSS:
1255         if input_api.re.search(deprecated_value, line):
1256           results.append(output_api.PresubmitError(
1257               "%s:%d: Use of deprecated CSS %s, use %s instead" %
1258               (fpath.LocalPath(), line_num, deprecated_value, value)))
1259   return results
1260
1261 def _CommonChecks(input_api, output_api):
1262   """Checks common to both upload and commit."""
1263   results = []
1264   results.extend(input_api.canned_checks.PanProjectChecks(
1265       input_api, output_api,
1266       excluded_paths=_EXCLUDED_PATHS + _TESTRUNNER_PATHS))
1267   results.extend(_CheckAuthorizedAuthor(input_api, output_api))
1268   results.extend(
1269       _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
1270   results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
1271   results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
1272   results.extend(_CheckNoNewWStrings(input_api, output_api))
1273   results.extend(_CheckNoDEPSGIT(input_api, output_api))
1274   results.extend(_CheckNoBannedFunctions(input_api, output_api))
1275   results.extend(_CheckNoPragmaOnce(input_api, output_api))
1276   results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
1277   results.extend(_CheckUnwantedDependencies(input_api, output_api))
1278   results.extend(_CheckFilePermissions(input_api, output_api))
1279   results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
1280   results.extend(_CheckIncludeOrder(input_api, output_api))
1281   results.extend(_CheckForVersionControlConflicts(input_api, output_api))
1282   results.extend(_CheckPatchFiles(input_api, output_api))
1283   results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
1284   results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
1285   results.extend(_CheckForInvalidOSMacros(input_api, output_api))
1286   results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1287   results.extend(
1288       input_api.canned_checks.CheckChangeHasNoTabs(
1289           input_api,
1290           output_api,
1291           source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
1292   results.extend(_CheckSpamLogging(input_api, output_api))
1293   results.extend(_CheckForAnonymousVariables(input_api, output_api))
1294   results.extend(_CheckCygwinShell(input_api, output_api))
1295   results.extend(_CheckUserActionUpdate(input_api, output_api))
1296   results.extend(_CheckNoDeprecatedCSS(input_api, output_api))
1297   results.extend(_CheckParseErrors(input_api, output_api))
1298   results.extend(_CheckForIPCRules(input_api, output_api))
1299
1300   if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
1301     results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
1302         input_api, output_api,
1303         input_api.PresubmitLocalPath(),
1304         whitelist=[r'^PRESUBMIT_test\.py$']))
1305   return results
1306
1307
1308 def _CheckAuthorizedAuthor(input_api, output_api):
1309   """For non-googler/chromites committers, verify the author's email address is
1310   in AUTHORS.
1311   """
1312   # TODO(maruel): Add it to input_api?
1313   import fnmatch
1314
1315   author = input_api.change.author_email
1316   if not author:
1317     input_api.logging.info('No author, skipping AUTHOR check')
1318     return []
1319   authors_path = input_api.os_path.join(
1320       input_api.PresubmitLocalPath(), 'AUTHORS')
1321   valid_authors = (
1322       input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
1323       for line in open(authors_path))
1324   valid_authors = [item.group(1).lower() for item in valid_authors if item]
1325   if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
1326     input_api.logging.info('Valid authors are %s', ', '.join(valid_authors))
1327     return [output_api.PresubmitPromptWarning(
1328         ('%s is not in AUTHORS file. If you are a new contributor, please visit'
1329         '\n'
1330         'http://www.chromium.org/developers/contributing-code and read the '
1331         '"Legal" section\n'
1332         'If you are a chromite, verify the contributor signed the CLA.') %
1333         author)]
1334   return []
1335
1336
1337 def _CheckPatchFiles(input_api, output_api):
1338   problems = [f.LocalPath() for f in input_api.AffectedFiles()
1339       if f.LocalPath().endswith(('.orig', '.rej'))]
1340   if problems:
1341     return [output_api.PresubmitError(
1342         "Don't commit .rej and .orig files.", problems)]
1343   else:
1344     return []
1345
1346
1347 def _DidYouMeanOSMacro(bad_macro):
1348   try:
1349     return {'A': 'OS_ANDROID',
1350             'B': 'OS_BSD',
1351             'C': 'OS_CHROMEOS',
1352             'F': 'OS_FREEBSD',
1353             'L': 'OS_LINUX',
1354             'M': 'OS_MACOSX',
1355             'N': 'OS_NACL',
1356             'O': 'OS_OPENBSD',
1357             'P': 'OS_POSIX',
1358             'S': 'OS_SOLARIS',
1359             'W': 'OS_WIN'}[bad_macro[3].upper()]
1360   except KeyError:
1361     return ''
1362
1363
1364 def _CheckForInvalidOSMacrosInFile(input_api, f):
1365   """Check for sensible looking, totally invalid OS macros."""
1366   preprocessor_statement = input_api.re.compile(r'^\s*#')
1367   os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
1368   results = []
1369   for lnum, line in f.ChangedContents():
1370     if preprocessor_statement.search(line):
1371       for match in os_macro.finditer(line):
1372         if not match.group(1) in _VALID_OS_MACROS:
1373           good = _DidYouMeanOSMacro(match.group(1))
1374           did_you_mean = ' (did you mean %s?)' % good if good else ''
1375           results.append('    %s:%d %s%s' % (f.LocalPath(),
1376                                              lnum,
1377                                              match.group(1),
1378                                              did_you_mean))
1379   return results
1380
1381
1382 def _CheckForInvalidOSMacros(input_api, output_api):
1383   """Check all affected files for invalid OS macros."""
1384   bad_macros = []
1385   for f in input_api.AffectedFiles():
1386     if not f.LocalPath().endswith(('.py', '.js', '.html', '.css')):
1387       bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
1388
1389   if not bad_macros:
1390     return []
1391
1392   return [output_api.PresubmitError(
1393       'Possibly invalid OS macro[s] found. Please fix your code\n'
1394       'or add your macro to src/PRESUBMIT.py.', bad_macros)]
1395
1396 def _CheckForIPCRules(input_api, output_api):
1397   """Check for same IPC rules described in
1398   http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
1399   """
1400   base_pattern = r'IPC_ENUM_TRAITS\('
1401   inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
1402   comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
1403
1404   problems = []
1405   for f in input_api.AffectedSourceFiles(None):
1406     local_path = f.LocalPath()
1407     if not local_path.endswith('.h'):
1408       continue
1409     for line_number, line in f.ChangedContents():
1410       if inclusion_pattern.search(line) and not comment_pattern.search(line):
1411         problems.append(
1412           '%s:%d\n    %s' % (local_path, line_number, line.strip()))
1413
1414   if problems:
1415     return [output_api.PresubmitPromptWarning(
1416         _IPC_ENUM_TRAITS_DEPRECATED, problems)]
1417   else:
1418     return []
1419
1420
1421 def CheckChangeOnUpload(input_api, output_api):
1422   results = []
1423   results.extend(_CommonChecks(input_api, output_api))
1424   results.extend(_CheckValidHostsInDEPS(input_api, output_api))
1425   results.extend(_CheckJavaStyle(input_api, output_api))
1426   return results
1427
1428
1429 def GetTryServerMasterForBot(bot):
1430   """Returns the Try Server master for the given bot.
1431
1432   It tries to guess the master from the bot name, but may still fail
1433   and return None.  There is no longer a default master.
1434   """
1435   # Potentially ambiguous bot names are listed explicitly.
1436   master_map = {
1437       'linux_gpu': 'tryserver.chromium.gpu',
1438       'mac_gpu': 'tryserver.chromium.gpu',
1439       'win_gpu': 'tryserver.chromium.gpu',
1440       'chromium_presubmit': 'tryserver.chromium.linux',
1441       'blink_presubmit': 'tryserver.chromium.linux',
1442       'tools_build_presubmit': 'tryserver.chromium.linux',
1443   }
1444   master = master_map.get(bot)
1445   if not master:
1446     if 'gpu' in bot:
1447       master = 'tryserver.chromium.gpu'
1448     elif 'linux' in bot or 'android' in bot or 'presubmit' in bot:
1449       master = 'tryserver.chromium.linux'
1450     elif 'win' in bot:
1451       master = 'tryserver.chromium.win'
1452     elif 'mac' in bot or 'ios' in bot:
1453       master = 'tryserver.chromium.mac'
1454   return master
1455
1456
1457 def GetDefaultTryConfigs(bots=None):
1458   """Returns a list of ('bot', set(['tests']), optionally filtered by [bots].
1459
1460   To add tests to this list, they MUST be in the the corresponding master's
1461   gatekeeper config. For example, anything on master.chromium would be closed by
1462   tools/build/masters/master.chromium/master_gatekeeper_cfg.py.
1463
1464   If 'bots' is specified, will only return configurations for bots in that list.
1465   """
1466
1467   standard_tests = [
1468       'base_unittests',
1469       'browser_tests',
1470       'cacheinvalidation_unittests',
1471       'check_deps',
1472       'check_deps2git',
1473       'content_browsertests',
1474       'content_unittests',
1475       'crypto_unittests',
1476       'gpu_unittests',
1477       'interactive_ui_tests',
1478       'ipc_tests',
1479       'jingle_unittests',
1480       'media_unittests',
1481       'net_unittests',
1482       'ppapi_unittests',
1483       'printing_unittests',
1484       'sql_unittests',
1485       'sync_unit_tests',
1486       'unit_tests',
1487       # Broken in release.
1488       #'url_unittests',
1489       #'webkit_unit_tests',
1490   ]
1491
1492   builders_and_tests = {
1493       # TODO(maruel): Figure out a way to run 'sizes' where people can
1494       # effectively update the perf expectation correctly.  This requires a
1495       # clobber=True build running 'sizes'. 'sizes' is not accurate with
1496       # incremental build. Reference:
1497       # http://chromium.org/developers/tree-sheriffs/perf-sheriffs.
1498       # TODO(maruel): An option would be to run 'sizes' but not count a failure
1499       # of this step as a try job failure.
1500       'android_aosp': ['compile'],
1501       'android_arm64_dbg_recipe': ['slave_steps'],
1502       'android_chromium_gn_compile_dbg': ['compile'],
1503       'android_chromium_gn_compile_rel': ['compile'],
1504       'android_clang_dbg': ['slave_steps'],
1505       'android_clang_dbg_recipe': ['slave_steps'],
1506       'android_dbg_tests_recipe': ['slave_steps'],
1507       'cros_x86': ['defaulttests'],
1508       'ios_dbg_simulator': [
1509           'compile',
1510           'base_unittests',
1511           'content_unittests',
1512           'crypto_unittests',
1513           'url_unittests',
1514           'net_unittests',
1515           'sql_unittests',
1516           'ui_unittests',
1517       ],
1518       'ios_rel_device': ['compile'],
1519       'ios_rel_device_ninja': ['compile'],
1520       'linux_asan': ['compile'],
1521       'mac_asan': ['compile'],
1522       #TODO(stip): Change the name of this builder to reflect that it's release.
1523       'linux_gtk': standard_tests,
1524       'linux_chromeos_asan': ['compile'],
1525       'linux_chromium_chromeos_clang_dbg': ['defaulttests'],
1526       'linux_chromium_chromeos_rel_swarming': ['defaulttests'],
1527       'linux_chromium_compile_dbg': ['defaulttests'],
1528       'linux_chromium_gn_dbg': ['compile'],
1529       'linux_chromium_gn_rel': ['defaulttests'],
1530       'linux_chromium_rel_swarming': ['defaulttests'],
1531       'linux_chromium_clang_dbg': ['defaulttests'],
1532       'linux_gpu': ['defaulttests'],
1533       'linux_nacl_sdk_build': ['compile'],
1534       'mac_chromium_compile_dbg': ['defaulttests'],
1535       'mac_chromium_rel_swarming': ['defaulttests'],
1536       'mac_gpu': ['defaulttests'],
1537       'mac_nacl_sdk_build': ['compile'],
1538       'win_chromium_compile_dbg': ['defaulttests'],
1539       'win_chromium_dbg': ['defaulttests'],
1540       'win_chromium_rel_swarming': ['defaulttests'],
1541       'win_chromium_x64_rel_swarming': ['defaulttests'],
1542       'win_gpu': ['defaulttests'],
1543       'win_nacl_sdk_build': ['compile'],
1544       'win8_chromium_rel': ['defaulttests'],
1545   }
1546
1547   if bots:
1548     filtered_builders_and_tests = dict((bot, set(builders_and_tests[bot]))
1549                                        for bot in bots)
1550   else:
1551     filtered_builders_and_tests = dict(
1552         (bot, set(tests))
1553         for bot, tests in builders_and_tests.iteritems())
1554
1555   # Build up the mapping from tryserver master to bot/test.
1556   out = dict()
1557   for bot, tests in filtered_builders_and_tests.iteritems():
1558     out.setdefault(GetTryServerMasterForBot(bot), {})[bot] = tests
1559   return out
1560
1561
1562 def CheckChangeOnCommit(input_api, output_api):
1563   results = []
1564   results.extend(_CommonChecks(input_api, output_api))
1565   # TODO(thestig) temporarily disabled, doesn't work in third_party/
1566   #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
1567   #    input_api, output_api, sources))
1568   # Make sure the tree is 'open'.
1569   results.extend(input_api.canned_checks.CheckTreeIsOpen(
1570       input_api,
1571       output_api,
1572       json_url='http://chromium-status.appspot.com/current?format=json'))
1573
1574   results.extend(input_api.canned_checks.CheckChangeHasBugField(
1575       input_api, output_api))
1576   results.extend(input_api.canned_checks.CheckChangeHasDescription(
1577       input_api, output_api))
1578   return results
1579
1580
1581 def GetPreferredTryMasters(project, change):
1582   files = change.LocalPaths()
1583
1584   if not files or all(re.search(r'[\\\/]OWNERS$', f) for f in files):
1585     return {}
1586
1587   if all(re.search(r'\.(m|mm)$|(^|[\\\/_])mac[\\\/_.]', f) for f in files):
1588     return GetDefaultTryConfigs([
1589         'mac_chromium_compile_dbg',
1590         'mac_chromium_rel_swarming',
1591     ])
1592   if all(re.search('(^|[/_])win[/_.]', f) for f in files):
1593     return GetDefaultTryConfigs([
1594         'win_chromium_dbg',
1595         'win_chromium_rel_swarming',
1596         'win8_chromium_rel',
1597     ])
1598   if all(re.search(r'(^|[\\\/_])android[\\\/_.]', f) for f in files):
1599     return GetDefaultTryConfigs([
1600         'android_aosp',
1601         'android_clang_dbg',
1602         'android_dbg_tests_recipe',
1603     ])
1604   if all(re.search(r'[\\\/_]ios[\\\/_.]', f) for f in files):
1605     return GetDefaultTryConfigs(['ios_rel_device', 'ios_dbg_simulator'])
1606
1607   builders = [
1608       'android_arm64_dbg_recipe',
1609       'android_chromium_gn_compile_rel',
1610       'android_chromium_gn_compile_dbg',
1611       'android_clang_dbg',
1612       'android_clang_dbg_recipe',
1613       'android_dbg_tests_recipe',
1614       'ios_dbg_simulator',
1615       'ios_rel_device',
1616       'ios_rel_device_ninja',
1617       'linux_chromium_chromeos_rel_swarming',
1618       'linux_chromium_clang_dbg',
1619       'linux_chromium_gn_dbg',
1620       'linux_chromium_gn_rel',
1621       'linux_chromium_rel_swarming',
1622       'linux_gpu',
1623       'mac_chromium_compile_dbg',
1624       'mac_chromium_rel_swarming',
1625       'mac_gpu',
1626       'win_chromium_compile_dbg',
1627       'win_chromium_rel_swarming',
1628       'win_chromium_x64_rel_swarming',
1629       'win_gpu',
1630       'win8_chromium_rel',
1631   ]
1632
1633   # Match things like path/aura/file.cc and path/file_aura.cc.
1634   # Same for chromeos.
1635   if any(re.search(r'[\\\/_](aura|chromeos)', f) for f in files):
1636     builders.extend([
1637         'linux_chromeos_asan',
1638         'linux_chromium_chromeos_clang_dbg'
1639     ])
1640
1641   # If there are gyp changes to base, build, or chromeos, run a full cros build
1642   # in addition to the shorter linux_chromeos build. Changes to high level gyp
1643   # files have a much higher chance of breaking the cros build, which is
1644   # differnt from the linux_chromeos build that most chrome developers test
1645   # with.
1646   if any(re.search('^(base|build|chromeos).*\.gypi?$', f) for f in files):
1647     builders.extend(['cros_x86'])
1648
1649   # The AOSP bot doesn't build the chrome/ layer, so ignore any changes to it
1650   # unless they're .gyp(i) files as changes to those files can break the gyp
1651   # step on that bot.
1652   if (not all(re.search('^chrome', f) for f in files) or
1653       any(re.search('\.gypi?$', f) for f in files)):
1654     builders.extend(['android_aosp'])
1655
1656   return GetDefaultTryConfigs(builders)