Add presubmit check for GN headers and code formatting.

These two checks are lifted directly from the Chromium code base. Also
adds a presubmit check for a patch description. Also adds some checks
to the "on commit" function so "git cl presubmit" can work.

Also reformats the style of the presubmit script a bit to match the
patterns in the Chromium presubmit.

Bug: angleproject:2626
Bug: angleproject:3054
Change-Id: Iff29b8856cf9eb9531e893cd0b0d80c0834b7676
Reviewed-on: https://chromium-review.googlesource.com/c/1403255
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index d904a31..8a9d874 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -10,7 +10,16 @@
 
 from subprocess import call
 
-def TestCodeGeneration(input_api, output_api):
+
+# Fragment of a regular expression that matches C++ and Objective-C++ implementation files.
+_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
+
+
+# Fragment of a regular expression that matches C++ and Objective-C++ header files.
+_HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
+
+
+def _CheckCodeGeneration(input_api, output_api):
     code_gen_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
                                            'scripts/run_code_generation.py')
     cmd_name = 'run_code_generation'
@@ -24,13 +33,67 @@
         print('Running ' + cmd_name)
     return input_api.RunTests([test_cmd])
 
-def CheckChangeOnUpload(input_api, output_api):
-    output = TestCodeGeneration(input_api, output_api)
 
-    if not input_api.change.BUG:
-        output += [output_api.PresubmitError(
-            'Must provide a Bug: line.')]
-    return output
+# Taken directly from Chromium's PRESUBMIT.py
+def _CheckNewHeaderWithoutGnChange(input_api, output_api):
+  """Checks that newly added header files have corresponding GN changes.
+  Note that this is only a heuristic. To be precise, run script:
+  build/check_gn_headers.py.
+  """
+
+  def headers(f):
+    return input_api.FilterSourceFile(
+      f, white_list=(r'.+%s' % _HEADER_EXTENSIONS, ))
+
+  new_headers = []
+  for f in input_api.AffectedSourceFiles(headers):
+    if f.Action() != 'A':
+      continue
+    new_headers.append(f.LocalPath())
+
+  def gn_files(f):
+    return input_api.FilterSourceFile(f, white_list=(r'.+\.gn', ))
+
+  all_gn_changed_contents = ''
+  for f in input_api.AffectedSourceFiles(gn_files):
+    for _, line in f.ChangedContents():
+      all_gn_changed_contents += line
+
+  problems = []
+  for header in new_headers:
+    basename = input_api.os_path.basename(header)
+    if basename not in all_gn_changed_contents:
+      problems.append(header)
+
+  if problems:
+    return [output_api.PresubmitPromptWarning(
+      'Missing GN changes for new header files', items=sorted(problems),
+      long_text='Please double check whether newly added header files need '
+      'corresponding changes in gn or gni files.\nThis checking is only a '
+      'heuristic. Run build/check_gn_headers.py to be precise.\n'
+      'Read https://crbug.com/661774 for more info.')]
+  return []
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    results = []
+    results.extend(_CheckCodeGeneration(input_api, output_api))
+    results.extend(input_api.canned_checks.CheckChangeHasBugField(
+      input_api, output_api))
+    results.extend(input_api.canned_checks.CheckChangeHasDescription(
+      input_api, output_api))
+    results.extend(_CheckNewHeaderWithoutGnChange(input_api, output_api))
+    results.extend(
+      input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
+    return results
+
 
 def CheckChangeOnCommit(input_api, output_api):
-    return []
+    results = []
+    results.extend(
+      input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
+    results.extend(input_api.canned_checks.CheckChangeHasBugField(
+      input_api, output_api))
+    results.extend(input_api.canned_checks.CheckChangeHasDescription(
+      input_api, output_api))
+    return results