{"id":9634,"url":"https://patchwork.libcamera.org/api/1.1/patches/9634/?format=json","web_url":"https://patchwork.libcamera.org/patch/9634/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200915142038.28757-3-paul.elder@ideasonboard.com>","date":"2020-09-15T14:20:17","name":"[libcamera-devel,02/23] utils: ipc: import mojo","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"66e2ff0dd3d933d7a8c74a0c7ccda9b19f4ed402","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/1.1/people/17/?format=json","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/9634/mbox/","series":[{"id":1291,"url":"https://patchwork.libcamera.org/api/1.1/series/1291/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=1291","date":"2020-09-15T14:20:16","name":"IPA isolation implementation","version":1,"mbox":"https://patchwork.libcamera.org/series/1291/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/9634/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/9634/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 9F3C6BF01C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 15 Sep 2020 14:21:51 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 62EAC62E31;\n\tTue, 15 Sep 2020 16:21:51 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E9F73603EE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 15 Sep 2020 16:21:01 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 03997FD8;\n\tTue, 15 Sep 2020 16:20:54 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"uUWIUCqZ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1600179658;\n\tbh=hJh9ZMbNHdXhItHEdwNfm79bFOiu6aKvOrR+LQrE4lQ=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=uUWIUCqZU0VLfD4YcTeGmRHX/O+lLpx8ZSRdYjZfuE2s0ys0QrCgou7Md9gtMp0/A\n\tcbeEFLhNWjdC2x608kZSWxnM/GO5d97+me+fIUshua1RrqXUPSjRMGsSXBJmh5MsP6\n\tHYxVbN3U1wX2NVUK/A427pEba1S5xLEd9LswlecY=","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Tue, 15 Sep 2020 23:20:17 +0900","Message-Id":"<20200915142038.28757-3-paul.elder@ideasonboard.com>","X-Mailer":"git-send-email 2.27.0","In-Reply-To":"<20200915142038.28757-1-paul.elder@ideasonboard.com>","References":"<20200915142038.28757-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","X-Mailman-Approved-At":"Tue, 15 Sep 2020 16:21:50 +0200","Subject":"[libcamera-devel] [PATCH 02/23] utils: ipc: import mojo","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Import mojo from Chromium repository, so that we can use it for\ngenerating code for the IPC mechanism.\n\nThis tree has been pruned somewhat; for example, the templates for code\ngeneration for unused languages are removed.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n utils/ipc/mojo/public/LICENSE                 |   27 +\n utils/ipc/mojo/public/tools/.style.yapf       |    6 +\n utils/ipc/mojo/public/tools/BUILD.gn          |   18 +\n utils/ipc/mojo/public/tools/bindings/BUILD.gn |  108 +\n .../ipc/mojo/public/tools/bindings/README.md  |  816 +++++++\n .../chromium_bindings_configuration.gni       |   51 +\n .../tools/bindings/compile_typescript.py      |   27 +\n .../tools/bindings/concatenate-files.py       |   54 +\n ...concatenate_and_replace_closure_exports.py |   73 +\n .../bindings/format_typemap_generator_args.py |   36 +\n .../tools/bindings/gen_data_files_list.py     |   52 +\n .../tools/bindings/generate_type_mappings.py  |  187 ++\n .../ipc/mojo/public/tools/bindings/mojom.gni  | 1941 +++++++++++++++++\n .../bindings/mojom_bindings_generator.py      |  390 ++++\n .../mojom_bindings_generator_unittest.py      |   62 +\n .../tools/bindings/mojom_types_downgrader.py  |  119 +\n .../tools/bindings/validate_typemap_config.py |   57 +\n utils/ipc/mojo/public/tools/mojom/README.md   |   14 +\n .../mojom/check_stable_mojom_compatibility.py |  170 ++\n ...eck_stable_mojom_compatibility_unittest.py |  260 +++\n .../mojo/public/tools/mojom/const_unittest.py |   90 +\n .../mojo/public/tools/mojom/enum_unittest.py  |   92 +\n .../mojo/public/tools/mojom/mojom/BUILD.gn    |   43 +\n .../mojo/public/tools/mojom/mojom/__init__.py |    0\n .../mojo/public/tools/mojom/mojom/error.py    |   28 +\n .../mojo/public/tools/mojom/mojom/fileutil.py |   45 +\n .../tools/mojom/mojom/fileutil_unittest.py    |   40 +\n .../tools/mojom/mojom/generate/__init__.py    |    0\n .../mojom/mojom/generate/constant_resolver.py |   93 +\n .../tools/mojom/mojom/generate/generator.py   |  325 +++\n .../mojom/generate/generator_unittest.py      |   74 +\n .../tools/mojom/mojom/generate/module.py      | 1635 ++++++++++++++\n .../mojom/mojom/generate/module_unittest.py   |   31 +\n .../public/tools/mojom/mojom/generate/pack.py |  258 +++\n .../mojom/mojom/generate/pack_unittest.py     |  225 ++\n .../mojom/mojom/generate/template_expander.py |   83 +\n .../tools/mojom/mojom/generate/translate.py   |  854 ++++++++\n .../mojom/generate/translate_unittest.py      |   73 +\n .../tools/mojom/mojom/parse/__init__.py       |    0\n .../public/tools/mojom/mojom/parse/ast.py     |  427 ++++\n .../tools/mojom/mojom/parse/ast_unittest.py   |  121 +\n .../mojom/mojom/parse/conditional_features.py |   82 +\n .../parse/conditional_features_unittest.py    |  233 ++\n .../public/tools/mojom/mojom/parse/lexer.py   |  251 +++\n .../tools/mojom/mojom/parse/lexer_unittest.py |  198 ++\n .../public/tools/mojom/mojom/parse/parser.py  |  488 +++++\n .../mojom/mojom/parse/parser_unittest.py      | 1390 ++++++++++++\n .../mojo/public/tools/mojom/mojom_parser.py   |  361 +++\n .../tools/mojom/mojom_parser_test_case.py     |   73 +\n .../tools/mojom/mojom_parser_unittest.py      |  171 ++\n .../tools/mojom/stable_attribute_unittest.py  |  127 ++\n .../mojom/version_compatibility_unittest.py   |  397 ++++\n .../public/tools/run_all_python_unittests.py  |   28 +\n utils/ipc/tools/diagnosis/crbug_1001171.py    |   51 +\n 54 files changed, 12855 insertions(+)\n create mode 100644 utils/ipc/mojo/public/LICENSE\n create mode 100644 utils/ipc/mojo/public/tools/.style.yapf\n create mode 100644 utils/ipc/mojo/public/tools/BUILD.gn\n create mode 100644 utils/ipc/mojo/public/tools/bindings/BUILD.gn\n create mode 100644 utils/ipc/mojo/public/tools/bindings/README.md\n create mode 100644 utils/ipc/mojo/public/tools/bindings/chromium_bindings_configuration.gni\n create mode 100644 utils/ipc/mojo/public/tools/bindings/compile_typescript.py\n create mode 100755 utils/ipc/mojo/public/tools/bindings/concatenate-files.py\n create mode 100755 utils/ipc/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py\n create mode 100755 utils/ipc/mojo/public/tools/bindings/format_typemap_generator_args.py\n create mode 100644 utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py\n create mode 100755 utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py\n create mode 100644 utils/ipc/mojo/public/tools/bindings/mojom.gni\n create mode 100755 utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator.py\n create mode 100644 utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py\n create mode 100755 utils/ipc/mojo/public/tools/bindings/mojom_types_downgrader.py\n create mode 100755 utils/ipc/mojo/public/tools/bindings/validate_typemap_config.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/README.md\n create mode 100755 utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py\n create mode 100755 utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/const_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/enum_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/__init__.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/error.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/__init__.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/constant_resolver.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/__init__.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py\n create mode 100755 utils/ipc/mojo/public/tools/mojom/mojom_parser.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom_parser_test_case.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom_parser_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/stable_attribute_unittest.py\n create mode 100644 utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\n create mode 100755 utils/ipc/mojo/public/tools/run_all_python_unittests.py\n create mode 100644 utils/ipc/tools/diagnosis/crbug_1001171.py","diff":"diff --git a/utils/ipc/mojo/public/LICENSE b/utils/ipc/mojo/public/LICENSE\nnew file mode 100644\nindex 00000000..972bb2ed\n--- /dev/null\n+++ b/utils/ipc/mojo/public/LICENSE\n@@ -0,0 +1,27 @@\n+// Copyright 2014 The Chromium Authors. All rights reserved.\n+//\n+// Redistribution and use in source and binary forms, with or without\n+// modification, are permitted provided that the following conditions are\n+// met:\n+//\n+//    * Redistributions of source code must retain the above copyright\n+// notice, this list of conditions and the following disclaimer.\n+//    * Redistributions in binary form must reproduce the above\n+// copyright notice, this list of conditions and the following disclaimer\n+// in the documentation and/or other materials provided with the\n+// distribution.\n+//    * Neither the name of Google Inc. nor the names of its\n+// contributors may be used to endorse or promote products derived from\n+// this software without specific prior written permission.\n+//\n+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\ndiff --git a/utils/ipc/mojo/public/tools/.style.yapf b/utils/ipc/mojo/public/tools/.style.yapf\nnew file mode 100644\nindex 00000000..b4ebbe24\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/.style.yapf\n@@ -0,0 +1,6 @@\n+[style]\n+based_on_style = pep8\n+\n+# New directories should use a .style.yapf that does not include the following:\n+column_limit = 80\n+indent_width = 2\ndiff --git a/utils/ipc/mojo/public/tools/BUILD.gn b/utils/ipc/mojo/public/tools/BUILD.gn\nnew file mode 100644\nindex 00000000..4c68350b\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/BUILD.gn\n@@ -0,0 +1,18 @@\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+# The main target used to aggregate all unit tests for Python-based Mojo tools.\n+# This is used to generate a complete isolate which can be pushed to bots to run\n+# the tests.\n+group(\"mojo_python_unittests\") {\n+  data = [\n+    \"run_all_python_unittests.py\",\n+    \"//testing/scripts/common.py\",\n+    \"//testing/scripts/run_isolated_script_test.py\",\n+    \"//testing/test_env.py\",\n+    \"//testing/xvfb.py\",\n+  ]\n+  deps = [ \"//mojo/public/tools/mojom/mojom:tests\" ]\n+  data_deps = [ \"//third_party/catapult/third_party/typ/\" ]\n+}\ndiff --git a/utils/ipc/mojo/public/tools/bindings/BUILD.gn b/utils/ipc/mojo/public/tools/bindings/BUILD.gn\nnew file mode 100644\nindex 00000000..8ba6e922\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/BUILD.gn\n@@ -0,0 +1,108 @@\n+# Copyright 2016 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import(\"//mojo/public/tools/bindings/mojom.gni\")\n+import(\"//third_party/jinja2/jinja2.gni\")\n+\n+action(\"precompile_templates\") {\n+  sources = mojom_generator_sources\n+  sources += [\n+    \"$mojom_generator_root/generators/cpp_templates/enum_macros.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/enum_serialization_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/interface_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/interface_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/interface_macros.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/interface_proxy_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/interface_stub_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-forward.h.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-import-headers.h.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-params-data.h.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-shared-internal.h.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-shared-message-ids.h.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-shared.cc.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-shared.h.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-test-utils.cc.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module-test-utils.h.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module.cc.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/module.h.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_data_view_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_data_view_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_macros.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_traits_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_traits_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/struct_unserialized_message_context.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/union_data_view_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/union_data_view_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/union_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/union_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/union_serialization_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/union_traits_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/union_traits_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/validation_macros.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/wrapper_class_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/wrapper_class_template_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/wrapper_union_class_declaration.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/wrapper_union_class_definition.tmpl\",\n+    \"$mojom_generator_root/generators/cpp_templates/wrapper_union_class_template_definition.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/constant_definition.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/constants.java.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/data_types_definition.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/enum.java.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/enum_definition.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/header.java.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/interface.java.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/interface_definition.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/interface_internal.java.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/struct.java.tmpl\",\n+    \"$mojom_generator_root/generators/java_templates/union.java.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/enum_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/externs/interface_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/externs/module.externs.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/externs/struct_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/fuzzing.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/interface_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/enum_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/interface_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/module_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/mojom-lite.js.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/struct_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/union_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/module.amd.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/module_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/struct_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/union_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/validation_macros.tmpl\",\n+    \"$mojom_generator_root/generators/mojolpm_templates/mojolpm.cc.tmpl\",\n+    \"$mojom_generator_root/generators/mojolpm_templates/mojolpm.h.tmpl\",\n+    \"$mojom_generator_root/generators/mojolpm_templates/mojolpm.proto.tmpl\",\n+    \"$mojom_generator_root/generators/mojolpm_templates/mojolpm_from_proto_macros.tmpl\",\n+    \"$mojom_generator_root/generators/mojolpm_templates/mojolpm_macros.tmpl\",\n+    \"$mojom_generator_root/generators/mojolpm_templates/mojolpm_to_proto_macros.tmpl\",\n+    \"$mojom_generator_root/generators/mojolpm_templates/mojolpm_traits_specialization_macros.tmpl\",\n+    \"$mojom_generator_root/generators/ts_templates/module_definition.tmpl\",\n+    \"$mojom_generator_root/generators/ts_templates/mojom.tmpl\",\n+  ]\n+  script = mojom_generator_script\n+\n+  inputs = jinja2_sources\n+  outputs = [\n+    \"$target_gen_dir/cpp_templates.zip\",\n+    \"$target_gen_dir/java_templates.zip\",\n+    \"$target_gen_dir/mojolpm_templates.zip\",\n+    \"$target_gen_dir/js_templates.zip\",\n+    \"$target_gen_dir/ts_templates.zip\",\n+  ]\n+  args = [\n+    \"-o\",\n+    rebase_path(target_gen_dir, root_build_dir),\n+    \"--use_bundled_pylibs\",\n+    \"precompile\",\n+  ]\n+}\ndiff --git a/utils/ipc/mojo/public/tools/bindings/README.md b/utils/ipc/mojo/public/tools/bindings/README.md\nnew file mode 100644\nindex 00000000..1a3d5c58\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/README.md\n@@ -0,0 +1,816 @@\n+# Mojom Interface Definition Language (IDL)\n+This document is a subset of the [Mojo documentation](/mojo/README.md).\n+\n+[TOC]\n+\n+## Overview\n+\n+Mojom is the IDL for Mojo interfaces. Given a `.mojom` file, the\n+[bindings\n+generator](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/) can\n+output bindings for any supported language: **C++**, **JavaScript**, or\n+**Java**.\n+\n+For a trivial example consider the following hypothetical Mojom file we write to\n+`//services/widget/public/mojom/frobinator.mojom`:\n+\n+```\n+module widget.mojom;\n+\n+interface Frobinator {\n+  Frobinate();\n+};\n+```\n+\n+This defines a single [interface](#Interfaces) named `Frobinator` in a\n+[module](#Modules) named `widget.mojom` (and thus fully qualified in Mojom as\n+`widget.mojom.Frobinator`.) Note that many interfaces and/or other types of\n+definitions (structs, enums, *etc.*) may be included in a single Mojom file.\n+\n+If we add a corresponding GN target to\n+`//services/widget/public/mojom/BUILD.gn`:\n+\n+```\n+import(\"mojo/public/tools/bindings/mojom.gni\")\n+\n+mojom(\"mojom\") {\n+  sources = [\n+    \"frobinator.mojom\",\n+  ]\n+}\n+```\n+\n+and then build this target:\n+\n+```\n+ninja -C out/r services/widget/public/mojom\n+```\n+\n+we'll find several generated sources in our output directory:\n+\n+```\n+out/r/gen/services/widget/public/mojom/frobinator.mojom.cc\n+out/r/gen/services/widget/public/mojom/frobinator.mojom.h\n+out/r/gen/services/widget/public/mojom/frobinator.mojom-shared.h\n+etc...\n+```\n+\n+Each of these generated source modules includes a set of definitions\n+representing the Mojom contents in C++. You can also build or depend on suffixed\n+target names to get bindings for other languages. For example,\n+\n+```\n+ninja -C out/r services/widget/public/mojom:mojom_js\n+ninja -C out/r services/widget/public/mojom:mojom_java\n+```\n+\n+would generate JavaScript and Java bindings respectively, in the same generated\n+output directory.\n+\n+For more details regarding the generated\n+outputs please see\n+[documentation for individual target languages](#Generated-Code-For-Target-Languages).\n+\n+## Mojom Syntax\n+\n+Mojom IDL allows developers to define **structs**, **unions**, **interfaces**,\n+**constants**, and **enums**, all within the context of a **module**. These\n+definitions are used to generate code in the supported target languages at build\n+time.\n+\n+Mojom files may **import** other Mojom files in order to reference their\n+definitions.\n+\n+### Primitive Types\n+Mojom supports a few basic data types which may be composed into structs or used\n+for message parameters.\n+\n+| Type                          | Description\n+|-------------------------------|-------------------------------------------------------|\n+| `bool`                        | Boolean type (`true` or `false`.)\n+| `int8`, `uint8`               | Signed or unsigned 8-bit integer.\n+| `int16`, `uint16`             | Signed or unsigned 16-bit integer.\n+| `int32`, `uint32`             | Signed or unsigned 32-bit integer.\n+| `int64`, `uint64`             | Signed or unsigned 64-bit integer.\n+| `float`, `double`             | 32- or 64-bit floating point number.\n+| `string`                      | UTF-8 encoded string.\n+| `array<T>`                    | Array of any Mojom type *T*; for example, `array<uint8>` or `array<array<string>>`.\n+| `array<T, N>`                 | Fixed-length array of any Mojom type *T*. The parameter *N* must be an integral constant.\n+| `map<S, T>`                   | Associated array maping values of type *S* to values of type *T*. *S* may be a `string`, `enum`, or numeric type.\n+| `handle`                      | Generic Mojo handle. May be any type of handle, including a wrapped native platform handle.\n+| `handle<message_pipe>`        | Generic message pipe handle.\n+| `handle<shared_buffer>`       | Shared buffer handle.\n+| `handle<data_pipe_producer>`  | Data pipe producer handle.\n+| `handle<data_pipe_consumer>`  | Data pipe consumer handle.\n+| `handle<platform>`            | A native platform/OS handle.\n+| *`pending_remote<InterfaceType>`*             | Any user-defined Mojom interface type. This is sugar for a strongly-typed message pipe handle which should eventually be used to make outgoing calls on the interface.\n+| *`pending_receiver<InterfaceType>`*            | A pending receiver for any user-defined Mojom interface type. This is sugar for a more strongly-typed message pipe handle which is expected to receive request messages and should therefore eventually be bound to an implementation of the interface.\n+| *`pending_associated_remote<InterfaceType>`*  | An associated interface handle. See [Associated Interfaces](#Associated-Interfaces)\n+| *`pending_associated_receiver<InterfaceType>`* | A pending associated receiver. See [Associated Interfaces](#Associated-Interfaces)\n+| *T*?                          | An optional (nullable) value. Primitive numeric types (integers, floats, booleans, and enums) are not nullable. All other types are nullable.\n+\n+### Modules\n+\n+Every Mojom file may optionally specify a single **module** to which it belongs.\n+\n+This is used strictly for aggregaging all defined symbols therein within a\n+common Mojom namespace. The specific impact this has on generated binidngs code\n+varies for each target language. For example, if the following Mojom is used to\n+generate bindings:\n+\n+```\n+module business.stuff;\n+\n+interface MoneyGenerator {\n+  GenerateMoney();\n+};\n+```\n+\n+Generated C++ bindings will define a class interface `MoneyGenerator` in the\n+`business::stuff` namespace, while Java bindings will define an interface\n+`MoneyGenerator` in the `org.chromium.business.stuff` package. JavaScript\n+bindings at this time are unaffected by module declarations.\n+\n+**NOTE:** By convention in the Chromium codebase, **all** Mojom files should\n+declare a module name with at least (and preferrably exactly) one top-level name\n+as well as an inner `mojom` module suffix. *e.g.*, `chrome.mojom`,\n+`business.mojom`, *etc.*\n+\n+This convention makes it easy to tell which symbols are generated by Mojom when\n+reading non-Mojom code, and it also avoids namespace collisions in the fairly\n+common scenario where you have a real C++ or Java `Foo` along with a\n+corresponding Mojom `Foo` for its serialized representation.\n+\n+### Imports\n+\n+If your Mojom references definitions from other Mojom files, you must **import**\n+those files. Import syntax is as follows:\n+\n+```\n+import \"services/widget/public/mojom/frobinator.mojom\";\n+```\n+\n+Import paths are always relative to the top-level directory.\n+\n+Note that circular imports are **not** supported.\n+\n+### Structs\n+\n+Structs are defined using the **struct** keyword, and they provide a way to\n+group related fields together:\n+\n+``` cpp\n+struct StringPair {\n+  string first;\n+  string second;\n+};\n+```\n+\n+Struct fields may be comprised of any of the types listed above in the\n+[Primitive Types](#Primitive-Types) section.\n+\n+Default values may be specified as long as they are constant:\n+\n+``` cpp\n+struct Request {\n+  int32 id = -1;\n+  string details;\n+};\n+```\n+\n+What follows is a fairly\n+comprehensive example using the supported field types:\n+\n+``` cpp\n+struct StringPair {\n+  string first;\n+  string second;\n+};\n+\n+enum AnEnum {\n+  YES,\n+  NO\n+};\n+\n+interface SampleInterface {\n+  DoStuff();\n+};\n+\n+struct AllTheThings {\n+  // Note that these types can never be marked nullable!\n+  bool boolean_value;\n+  int8 signed_8bit_value = 42;\n+  uint8 unsigned_8bit_value;\n+  int16 signed_16bit_value;\n+  uint16 unsigned_16bit_value;\n+  int32 signed_32bit_value;\n+  uint32 unsigned_32bit_value;\n+  int64 signed_64bit_value;\n+  uint64 unsigned_64bit_value;\n+  float float_value_32bit;\n+  double float_value_64bit;\n+  AnEnum enum_value = AnEnum.YES;\n+\n+  // Strings may be nullable.\n+  string? maybe_a_string_maybe_not;\n+\n+  // Structs may contain other structs. These may also be nullable.\n+  StringPair some_strings;\n+  StringPair? maybe_some_more_strings;\n+\n+  // In fact structs can also be nested, though in practice you must always make\n+  // such fields nullable -- otherwise messages would need to be infinitely long\n+  // in order to pass validation!\n+  AllTheThings? more_things;\n+\n+  // Arrays may be templated over any Mojom type, and are always nullable:\n+  array<int32> numbers;\n+  array<int32>? maybe_more_numbers;\n+\n+  // Arrays of arrays of arrays... are fine.\n+  array<array<array<AnEnum>>> this_works_but_really_plz_stop;\n+\n+  // The element type may be nullable if it's a type which is allowed to be\n+  // nullable.\n+  array<AllTheThings?> more_maybe_things;\n+\n+  // Fixed-size arrays get some extra validation on the receiving end to ensure\n+  // that the correct number of elements is always received.\n+  array<uint64, 2> uuid;\n+\n+  // Maps follow many of the same rules as arrays. Key types may be any\n+  // non-handle, non-collection type, and value types may be any supported\n+  // struct field type. Maps may also be nullable.\n+  map<string, int32> one_map;\n+  map<AnEnum, string>? maybe_another_map;\n+  map<StringPair, AllTheThings?>? maybe_a_pretty_weird_but_valid_map;\n+  map<StringPair, map<int32, array<map<string, string>?>?>?> ridiculous;\n+\n+  // And finally, all handle types are valid as struct fields and may be\n+  // nullable. Note that interfaces and interface requests (the \"Foo\" and\n+  // \"Foo&\" type syntax respectively) are just strongly-typed message pipe\n+  // handles.\n+  handle generic_handle;\n+  handle<data_pipe_consumer> reader;\n+  handle<data_pipe_producer>? maybe_writer;\n+  handle<shared_buffer> dumping_ground;\n+  handle<message_pipe> raw_message_pipe;\n+  pending_remote<SampleInterface>? maybe_a_sample_interface_client_pipe;\n+  pending_receiver<SampleInterface> non_nullable_sample_pending_receiver;\n+  pending_receiver<SampleInterface>? nullable_sample_pending_receiver;\n+  pending_associated_remote<SampleInterface> associated_interface_client;\n+  pending_associated_receiver<SampleInterface> associated_pending_receiver;\n+  pending_associated_receiver<SampleInterface>? maybe_another_pending_receiver;\n+};\n+```\n+\n+For details on how all of these different types translate to usable generated\n+code, see\n+[documentation for individual target languages](#Generated-Code-For-Target-Languages).\n+\n+### Unions\n+\n+Mojom supports tagged unions using the **union** keyword. A union is a\n+collection of fields which may taken the value of any single one of those fields\n+at a time. Thus they provide a way to represent a variant value type while\n+minimizing storage requirements.\n+\n+Union fields may be of any type supported by [struct](#Structs) fields. For\n+example:\n+\n+```cpp\n+union ExampleUnion {\n+  string str;\n+  StringPair pair;\n+  int64 id;\n+  array<uint64, 2> guid;\n+  SampleInterface iface;\n+};\n+```\n+\n+For details on how unions like this translate to generated bindings code, see\n+[documentation for individual target languages](#Generated-Code-For-Target-Languages).\n+\n+### Enumeration Types\n+\n+Enumeration types may be defined using the **enum** keyword either directly\n+within a module or nested within the namespace of some struct or interface:\n+\n+```\n+module business.mojom;\n+\n+enum Department {\n+  SALES = 0,\n+  DEV,\n+};\n+\n+struct Employee {\n+  enum Type {\n+    FULL_TIME,\n+    PART_TIME,\n+  };\n+\n+  Type type;\n+  // ...\n+};\n+```\n+\n+Similar to C-style enums, individual values may be explicitly assigned within an\n+enum definition. By default, values are based at zero and increment by\n+1 sequentially.\n+\n+The effect of nested definitions on generated bindings varies depending on the\n+target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages)\n+\n+### Constants\n+\n+Constants may be defined using the **const** keyword either directly within a\n+module or nested within the namespace of some struct or interface:\n+\n+```\n+module business.mojom;\n+\n+const string kServiceName = \"business\";\n+\n+struct Employee {\n+  const uint64 kInvalidId = 0;\n+\n+  enum Type {\n+    FULL_TIME,\n+    PART_TIME,\n+  };\n+\n+  uint64 id = kInvalidId;\n+  Type type;\n+};\n+```\n+\n+The effect of nested definitions on generated bindings varies depending on the\n+target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages)\n+\n+### Interfaces\n+\n+An **interface** is a logical bundle of parameterized request messages. Each\n+request message may optionally define a parameterized response message. Here's\n+an example to define an interface `Foo` with various kinds of requests:\n+\n+```\n+interface Foo {\n+  // A request which takes no arguments and expects no response.\n+  MyMessage();\n+\n+  // A request which has some arguments and expects no response.\n+  MyOtherMessage(string name, array<uint8> bytes);\n+\n+  // A request which expects a single-argument response.\n+  MyMessageWithResponse(string command) => (bool success);\n+\n+  // A request which expects a response with multiple arguments.\n+  MyMessageWithMoarResponse(string a, string b) => (int8 c, int8 d);\n+};\n+```\n+\n+Anything which is a valid struct field type (see [Structs](#Structs)) is also a\n+valid request or response argument type. The type notation is the same for both.\n+\n+### Attributes\n+\n+Mojom definitions may have their meaning altered by **attributes**, specified\n+with a syntax similar to Java or C# attributes. There are a handle of\n+interesting attributes supported today.\n+\n+**`[Sync]`**\n+:   The `Sync` attribute may be specified for any interface method which expects\n+    a response. This makes it so that callers of the method can wait\n+    synchronously for a response. See\n+    [Synchronous Calls](/mojo/public/cpp/bindings/README.md#Synchronous-Calls)\n+    in the C++ bindings documentation. Note that sync methods are only actually\n+    synchronous when called from C++.\n+\n+**`[Extensible]`**\n+:   The `Extensible` attribute may be specified for any enum definition. This\n+    essentially disables builtin range validation when receiving values of the\n+    enum type in a message, allowing older bindings to tolerate unrecognized\n+    values from newer versions of the enum.\n+\n+**`[Native]`**\n+:   The `Native` attribute may be specified for an empty struct declaration to\n+    provide a nominal bridge between Mojo IPC and legacy `IPC::ParamTraits` or\n+    `IPC_STRUCT_TRAITS*` macros.\n+    See\n+    [Repurposing Legacy IPC Traits](/docs/mojo_ipc_conversion.md#repurposing-and-invocations)\n+    for more details. Note support for this attribute is strictly limited to C++\n+    bindings generation.\n+\n+**`[MinVersion=N]`**\n+:   The `MinVersion` attribute is used to specify the version at which a given\n+    field, enum value, interface method, or method parameter was introduced.\n+    See [Versioning](#Versioning) for more details.\n+\n+**`[Stable]`**\n+:   The `Stable` attribute specifies that a given mojom type or interface\n+    definition can be considered stable over time, meaning it is safe to use for\n+    things like persistent storage or communication between independent\n+    version-skewed binaries. Stable definitions may only depend on builtin mojom\n+    types or other stable definitions, and changes to such definitions MUST\n+    preserve backward-compatibility through appropriate use of versioning.\n+    Backward-compatibility of changes is enforced in the Chromium tree using a\n+    strict presubmit check. See [Versioning](#Versioning) for more details on\n+    backward-compatibility constraints.\n+\n+**`[EnableIf=value]`**\n+:   The `EnableIf` attribute is used to conditionally enable definitions when\n+    the mojom is parsed. If the `mojom` target in the GN file does not include\n+    the matching `value` in the list of `enabled_features`, the definition\n+    will be disabled. This is useful for mojom definitions that only make\n+    sense on one platform. Note that the `EnableIf` attribute can only be set\n+    once per definition.\n+\n+## Generated Code For Target Languages\n+\n+When the bindings generator successfully processes an input Mojom file, it emits\n+corresponding code for each supported target language. For more details on how\n+Mojom concepts translate to a given target langauge, please refer to the\n+bindings API documentation for that language:\n+\n+* [C++ Bindings](/mojo/public/cpp/bindings/README.md)\n+* [JavaScript Bindings](/mojo/public/js/README.md)\n+* [Java Bindings](/mojo/public/java/bindings/README.md)\n+\n+## Message Validation\n+\n+Regardless of target language, all interface messages are validated during\n+deserialization before they are dispatched to a receiving implementation of the\n+interface. This helps to ensure consitent validation across interfaces without\n+leaving the burden to developers and security reviewers every time a new message\n+is added.\n+\n+If a message fails validation, it is never dispatched. Instead a **connection\n+error** is raised on the binding object (see\n+[C++ Connection Errors](/mojo/public/cpp/bindings/README.md#Connection-Errors),\n+[Java Connection Errors](/mojo/public/java/bindings/README.md#Connection-Errors),\n+or\n+[JavaScript Connection Errors](/mojo/public/js/README.md#Connection-Errors) for\n+details.)\n+\n+Some baseline level of validation is done automatically for primitive Mojom\n+types.\n+\n+### Non-Nullable Objects\n+\n+Mojom fields or parameter values (*e.g.*, structs, interfaces, arrays, *etc.*)\n+may be marked nullable in Mojom definitions (see\n+[Primitive Types](#Primitive-Types).) If a field or parameter is **not** marked\n+nullable but a message is received with a null value in its place, that message\n+will fail validation.\n+\n+### Enums\n+\n+Enums declared in Mojom are automatically validated against the range of legal\n+values. For example if a Mojom declares the enum:\n+\n+``` cpp\n+enum AdvancedBoolean {\n+  TRUE = 0,\n+  FALSE = 1,\n+  FILE_NOT_FOUND = 2,\n+};\n+```\n+\n+and a message is received with the integral value 3 (or anything other than 0,\n+1, or 2) in place of some `AdvancedBoolean` field or parameter, the message will\n+fail validation.\n+\n+*** note\n+NOTE: It's possible to avoid this type of validation error by explicitly marking\n+an enum as [Extensible](#Attributes) if you anticipate your enum being exchanged\n+between two different versions of the binding interface. See\n+[Versioning](#Versioning).\n+***\n+\n+### Other failures\n+\n+There are a  host of internal validation errors that may occur when a malformed\n+message is received, but developers should not be concerned with these\n+specifically; in general they can only result from internal bindings bugs,\n+compromised processes, or some remote endpoint making a dubious effort to\n+manually encode their own bindings messages.\n+\n+### Custom Validation\n+\n+It's also possible for developers to define custom validation logic for specific\n+Mojom struct types by exploiting the\n+[type mapping](/mojo/public/cpp/bindings/README.md#Type-Mapping) system for C++\n+bindings. Messages rejected by custom validation logic trigger the same\n+validation failure behavior as the built-in type validation routines.\n+\n+## Associated Interfaces\n+\n+As mentioned in the [Primitive Types](#Primitive-Types) section above, pending_remote\n+and pending_receiver fields and parameters may be marked as `associated`. This\n+essentially means that they are piggy-backed on some other interface's message\n+pipe.\n+\n+Because individual interface message pipes operate independently there can be no\n+relative ordering guarantees among them. Associated interfaces are useful when\n+one interface needs to guarantee strict FIFO ordering with respect to one or\n+more other interfaces, as they allow interfaces to share a single pipe.\n+\n+Currently associated interfaces are only supported in generated C++ bindings.\n+See the documentation for\n+[C++ Associated Interfaces](/mojo/public/cpp/bindings/README.md#Associated-Interfaces).\n+\n+## Versioning\n+\n+### Overview\n+\n+*** note\n+**NOTE:** You don't need to worry about versioning if you don't care about\n+backwards compatibility. Specifically, all parts of Chrome are updated\n+atomically today and there is not yet any possibility of any two Chrome\n+processes communicating with two different versions of any given Mojom\n+interface.\n+***\n+\n+Services extend their interfaces to support new features over time, and clients\n+want to use those new features when they are available. If services and clients\n+are not updated at the same time, it's important for them to be able to\n+communicate with each other using different snapshots (versions) of their\n+interfaces.\n+\n+This document shows how to extend Mojom interfaces in a backwards-compatible\n+way. Changing interfaces in a non-backwards-compatible way is not discussed,\n+because in that case communication between different interface versions is\n+impossible anyway.\n+\n+### Versioned Structs\n+\n+You can use the `MinVersion` [attribute](#Attributes) to indicate from which\n+version a struct field is introduced. Assume you have the following struct:\n+\n+``` cpp\n+struct Employee {\n+  uint64 employee_id;\n+  string name;\n+};\n+```\n+\n+and you would like to add a birthday field. You can do:\n+\n+``` cpp\n+struct Employee {\n+  uint64 employee_id;\n+  string name;\n+  [MinVersion=1] Date? birthday;\n+};\n+```\n+\n+By default, fields belong to version 0. New fields must be appended to the\n+struct definition (*i.e*., existing fields must not change **ordinal value**)\n+with the `MinVersion` attribute set to a number greater than any previous\n+existing versions.\n+\n+*** note\n+**NOTE:** do not change existing fields in versioned structs, as this is\n+not backwards-compatible. Instead, rename the old field to make its\n+deprecation clear and add a new field with the new version number.\n+***\n+\n+**Ordinal value** refers to the relative positional layout of a struct's fields\n+(and an interface's methods) when encoded in a message. Implicitly, ordinal\n+numbers are assigned to fields according to lexical position. In the example\n+above, `employee_id` has an ordinal value of 0 and `name` has an ordinal value\n+of 1.\n+\n+Ordinal values can be specified explicitly using `**@**` notation, subject to\n+the following hard constraints:\n+\n+* For any given struct or interface, if any field or method explicitly specifies\n+    an ordinal value, all fields or methods must explicitly specify an ordinal\n+    value.\n+* For an *N*-field struct or *N*-method interface, the set of explicitly\n+    assigned ordinal values must be limited to the range *[0, N-1]*. Interfaces\n+    should include placeholder methods to fill the ordinal positions of removed\n+    methods (for example \"Unused_Message_7@7()\" or \"RemovedMessage@42()\", etc).\n+\n+You may reorder fields, but you must ensure that the ordinal values of existing\n+fields remain unchanged. For example, the following struct remains\n+backwards-compatible:\n+\n+``` cpp\n+struct Employee {\n+  uint64 employee_id@0;\n+  [MinVersion=1] Date? birthday@2;\n+  string name@1;\n+};\n+```\n+\n+*** note\n+**NOTE:** Newly added fields of Mojo object or handle types MUST be nullable.\n+See [Primitive Types](#Primitive-Types).\n+***\n+\n+### Versioned Interfaces\n+\n+There are two dimensions on which an interface can be extended\n+\n+**Appending New Parameters To Existing Methods**\n+:   Parameter lists are treated as structs internally, so all the rules of\n+    versioned structs apply to method parameter lists. The only difference is\n+    that the version number is scoped to the whole interface rather than to any\n+    individual parameter list.\n+\n+    Please note that adding a response to a message which did not previously\n+    expect a response is a not a backwards-compatible change.\n+\n+**Appending New Methods**\n+:   Similarly, you can reorder methods with explicit ordinal values as long as\n+    the ordinal values of existing methods are unchanged.\n+\n+For example:\n+\n+``` cpp\n+// Old version:\n+interface HumanResourceDatabase {\n+  AddEmployee(Employee employee) => (bool success);\n+  QueryEmployee(uint64 id) => (Employee? employee);\n+};\n+\n+// New version:\n+interface HumanResourceDatabase {\n+  AddEmployee(Employee employee) => (bool success);\n+\n+  QueryEmployee(uint64 id, [MinVersion=1] bool retrieve_finger_print)\n+      => (Employee? employee,\n+          [MinVersion=1] array<uint8>? finger_print);\n+\n+  [MinVersion=1]\n+  AttachFingerPrint(uint64 id, array<uint8> finger_print)\n+      => (bool success);\n+};\n+```\n+\n+Similar to [versioned structs](#Versioned-Structs), when you pass the parameter\n+list of a request or response method to a destination using an older version of\n+an interface, unrecognized fields are silently discarded. However, if the method\n+call itself is not recognized, it is considered a validation error and the\n+receiver will close its end of the interface pipe. For example, if a client on\n+version 1 of the above interface sends an `AttachFingerPrint` request to an\n+implementation of version 0, the client will be disconnected.\n+\n+Bindings target languages that support versioning expose means to query or\n+assert the remote version from a client handle (*e.g.*, an\n+`mojo::Remote<T>` in C++ bindings.)\n+\n+See\n+[C++ Versioning Considerations](/mojo/public/cpp/bindings/README.md#Versioning-Considerations)\n+and\n+[Java Versioning Considerations](/mojo/public/java/bindings/README.md#Versioning-Considerations)\n+\n+### Versioned Enums\n+\n+**By default, enums are non-extensible**, which means that generated message\n+validation code does not expect to see new values in the future. When an unknown\n+value is seen for a non-extensible enum field or parameter, a validation error\n+is raised.\n+\n+If you want an enum to be extensible in the future, you can apply the\n+`[Extensible]` [attribute](#Attributes):\n+\n+``` cpp\n+[Extensible]\n+enum Department {\n+  SALES,\n+  DEV,\n+};\n+```\n+\n+And later you can extend this enum without breaking backwards compatibility:\n+\n+``` cpp\n+[Extensible]\n+enum Department {\n+  SALES,\n+  DEV,\n+  [MinVersion=1] RESEARCH,\n+};\n+```\n+\n+*** note\n+**NOTE:** For versioned enum definitions, the use of a `[MinVersion]` attribute\n+is strictly for documentation purposes. It has no impact on the generated code.\n+***\n+\n+With extensible enums, bound interface implementations may receive unknown enum\n+values and will need to deal with them gracefully. See\n+[C++ Versioning Considerations](/mojo/public/cpp/bindings/README.md#Versioning-Considerations)\n+for details.\n+\n+## Grammar Reference\n+\n+Below is the (BNF-ish) context-free grammar of the Mojom language:\n+\n+```\n+MojomFile = StatementList\n+StatementList = Statement StatementList | Statement\n+Statement = ModuleStatement | ImportStatement | Definition\n+\n+ModuleStatement = AttributeSection \"module\" Identifier \";\"\n+ImportStatement = \"import\" StringLiteral \";\"\n+Definition = Struct Union Interface Enum Const\n+\n+AttributeSection = <empty> | \"[\" AttributeList \"]\"\n+AttributeList = <empty> | NonEmptyAttributeList\n+NonEmptyAttributeList = Attribute\n+                      | Attribute \",\" NonEmptyAttributeList\n+Attribute = Name\n+          | Name \"=\" Name\n+          | Name \"=\" Literal\n+\n+Struct = AttributeSection \"struct\" Name \"{\" StructBody \"}\" \";\"\n+       | AttributeSection \"struct\" Name \";\"\n+StructBody = <empty>\n+           | StructBody Const\n+           | StructBody Enum\n+           | StructBody StructField\n+StructField = AttributeSection TypeSpec Name Ordinal Default \";\"\n+\n+Union = AttributeSection \"union\" Name \"{\" UnionBody \"}\" \";\"\n+UnionBody = <empty> | UnionBody UnionField\n+UnionField = AttributeSection TypeSpec Name Ordinal \";\"\n+\n+Interface = AttributeSection \"interface\" Name \"{\" InterfaceBody \"}\" \";\"\n+InterfaceBody = <empty>\n+              | InterfaceBody Const\n+              | InterfaceBody Enum\n+              | InterfaceBody Method\n+Method = AttributeSection Name Ordinal \"(\" ParamterList \")\" Response \";\"\n+ParameterList = <empty> | NonEmptyParameterList\n+NonEmptyParameterList = Parameter\n+                      | Parameter \",\" NonEmptyParameterList\n+Parameter = AttributeSection TypeSpec Name Ordinal\n+Response = <empty> | \"=>\" \"(\" ParameterList \")\"\n+\n+TypeSpec = TypeName \"?\" | TypeName\n+TypeName = BasicTypeName\n+         | Array\n+         | FixedArray\n+         | Map\n+         | InterfaceRequest\n+BasicTypeName = Identifier | \"associated\" Identifier | HandleType | NumericType\n+NumericType = \"bool\" | \"int8\" | \"uint8\" | \"int16\" | \"uint16\" | \"int32\"\n+            | \"uint32\" | \"int64\" | \"uint64\" | \"float\" | \"double\"\n+HandleType = \"handle\" | \"handle\" \"<\" SpecificHandleType \">\"\n+SpecificHandleType = \"message_pipe\"\n+                   | \"shared_buffer\"\n+                   | \"data_pipe_consumer\"\n+                   | \"data_pipe_producer\"\n+                   | \"platform\"\n+Array = \"array\" \"<\" TypeSpec \">\"\n+FixedArray = \"array\" \"<\" TypeSpec \",\" IntConstDec \">\"\n+Map = \"map\" \"<\" Identifier \",\" TypeSpec \">\"\n+InterfaceRequest = Identifier \"&\" | \"associated\" Identifier \"&\"\n+\n+Ordinal = <empty> | OrdinalValue\n+\n+Default = <empty> | \"=\" Constant\n+\n+Enum = AttributeSection \"enum\" Name \"{\" NonEmptyEnumValueList \"}\" \";\"\n+     | AttributeSection \"enum\" Name \"{\" NonEmptyEnumValueList \",\" \"}\" \";\"\n+NonEmptyEnumValueList = EnumValue | NonEmptyEnumValueList \",\" EnumValue\n+EnumValue = AttributeSection Name\n+          | AttributeSection Name \"=\" Integer\n+          | AttributeSection Name \"=\" Identifier\n+\n+Const = \"const\" TypeSpec Name \"=\" Constant \";\"\n+\n+Constant = Literal | Identifier \";\"\n+\n+Identifier = Name | Name \".\" Identifier\n+\n+Literal = Integer | Float | \"true\" | \"false\" | \"default\" | StringLiteral\n+\n+Integer = IntConst | \"+\" IntConst | \"-\" IntConst\n+IntConst = IntConstDec | IntConstHex\n+\n+Float = FloatConst | \"+\" FloatConst | \"-\" FloatConst\n+\n+; The rules below are for tokens matched strictly according to the given regexes\n+\n+Identifier = /[a-zA-Z_][0-9a-zA-Z_]*/\n+IntConstDec = /0|(1-9[0-9]*)/\n+IntConstHex = /0[xX][0-9a-fA-F]+/\n+OrdinalValue = /@(0|(1-9[0-9]*))/\n+FloatConst = ... # Imagine it's close enough to C-style float syntax.\n+StringLiteral = ... # Imagine it's close enough to C-style string literals, including escapes.\n+```\n+\n+## Additional Documentation\n+\n+[Mojom Message Format](https://docs.google.com/document/d/13pv9cFh5YKuBggDBQ1-AL8VReF-IYpFOFpRfvWFrwio/edit)\n+:    Describes the wire format used by Mojo bindings interfaces over message\n+     pipes.\n+\n+[Input Format of Mojom Message Validation Tests](https://docs.google.com/document/d/1-y-2IYctyX2NPaLxJjpJfzVNWCC2SR2MJAD9MpIytHQ/edit)\n+:    Describes a text format used to facilitate bindings message validation\n+     tests.\ndiff --git a/utils/ipc/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/utils/ipc/mojo/public/tools/bindings/chromium_bindings_configuration.gni\nnew file mode 100644\nindex 00000000..d8a13874\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/chromium_bindings_configuration.gni\n@@ -0,0 +1,51 @@\n+# Copyright 2016 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+_typemap_imports = [\n+  \"//chrome/chrome_cleaner/mojom/typemaps/typemaps.gni\",\n+  \"//chrome/common/importer/typemaps.gni\",\n+  \"//chrome/common/media_router/mojom/typemaps.gni\",\n+  \"//chrome/typemaps.gni\",\n+  \"//chromecast/typemaps.gni\",\n+  \"//chromeos/typemaps.gni\",\n+  \"//chromeos/components/multidevice/mojom/typemaps.gni\",\n+  \"//chromeos/services/cros_healthd/public/mojom/typemaps.gni\",\n+  \"//chromeos/services/device_sync/public/mojom/typemaps.gni\",\n+  \"//chromeos/services/network_config/public/mojom/typemaps.gni\",\n+  \"//chromeos/services/secure_channel/public/mojom/typemaps.gni\",\n+  \"//components/arc/mojom/typemaps.gni\",\n+  \"//components/chromeos_camera/common/typemaps.gni\",\n+  \"//components/services/storage/public/cpp/filesystem/typemaps.gni\",\n+  \"//components/sync/mojom/typemaps.gni\",\n+  \"//components/typemaps.gni\",\n+  \"//content/browser/typemaps.gni\",\n+  \"//content/public/common/typemaps.gni\",\n+  \"//sandbox/mac/mojom/typemaps.gni\",\n+  \"//services/media_session/public/cpp/typemaps.gni\",\n+  \"//services/proxy_resolver/public/cpp/typemaps.gni\",\n+  \"//services/resource_coordinator/public/cpp/typemaps.gni\",\n+  \"//services/service_manager/public/cpp/typemaps.gni\",\n+  \"//services/tracing/public/mojom/typemaps.gni\",\n+]\n+\n+_typemaps = []\n+foreach(typemap_import, _typemap_imports) {\n+  # Avoid reassignment error by assigning to empty scope first.\n+  _imported = {\n+  }\n+  _imported = read_file(typemap_import, \"scope\")\n+  _typemaps += _imported.typemaps\n+}\n+\n+typemaps = []\n+foreach(typemap, _typemaps) {\n+  typemaps += [\n+    {\n+      filename = typemap\n+      config = read_file(typemap, \"scope\")\n+    },\n+  ]\n+}\n+\n+component_macro_suffix = \"\"\ndiff --git a/utils/ipc/mojo/public/tools/bindings/compile_typescript.py b/utils/ipc/mojo/public/tools/bindings/compile_typescript.py\nnew file mode 100644\nindex 00000000..a978901b\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/compile_typescript.py\n@@ -0,0 +1,27 @@\n+# Copyright 2019 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import os\n+import sys\n+import argparse\n+\n+_HERE_PATH = os.path.dirname(__file__)\n+_SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..', '..'))\n+\n+sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node'))\n+import node\n+import node_modules\n+\n+def main(argv):\n+  parser = argparse.ArgumentParser()\n+  parser.add_argument('--tsconfig_path', required=True)\n+  args = parser.parse_args(argv)\n+\n+  result = node.RunNode([node_modules.PathToTypescript()] +\n+                        ['--project', args.tsconfig_path])\n+  if len(result) != 0:\n+    raise RuntimeError('Failed to compile Typescript: \\n%s' % result)\n+\n+if __name__ == '__main__':\n+  main(sys.argv[1:])\ndiff --git a/utils/ipc/mojo/public/tools/bindings/concatenate-files.py b/utils/ipc/mojo/public/tools/bindings/concatenate-files.py\nnew file mode 100755\nindex 00000000..48bc66fd\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/concatenate-files.py\n@@ -0,0 +1,54 @@\n+#!/usr/bin/env python\n+# Copyright 2019 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+#\n+# This utility concatenates several files into one. On Unix-like systems\n+# it is equivalent to:\n+#   cat file1 file2 file3 ...files... > target\n+#\n+# The reason for writing a separate utility is that 'cat' is not available\n+# on all supported build platforms, but Python is, and hence this provides\n+# us with an easy and uniform way of doing this on all platforms.\n+\n+# for py2/py3 compatibility\n+from __future__ import print_function\n+\n+import optparse\n+\n+\n+def Concatenate(filenames):\n+  \"\"\"Concatenate files.\n+\n+  Args:\n+    files: Array of file names.\n+           The last name is the target; all earlier ones are sources.\n+\n+  Returns:\n+    True, if the operation was successful.\n+  \"\"\"\n+  if len(filenames) < 2:\n+    print(\"An error occurred generating %s:\\nNothing to do.\" % filenames[-1])\n+    return False\n+\n+  try:\n+    with open(filenames[-1], \"wb\") as target:\n+      for filename in filenames[:-1]:\n+        with open(filename, \"rb\") as current:\n+          target.write(current.read())\n+    return True\n+  except IOError as e:\n+    print(\"An error occurred when writing %s:\\n%s\" % (filenames[-1], e))\n+    return False\n+\n+\n+def main():\n+  parser = optparse.OptionParser()\n+  parser.set_usage(\"\"\"Concatenate several files into one.\n+      Equivalent to: cat file1 ... > target.\"\"\")\n+  (_options, args) = parser.parse_args()\n+  exit(0 if Concatenate(args) else 1)\n+\n+\n+if __name__ == \"__main__\":\n+  main()\ndiff --git a/utils/ipc/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py b/utils/ipc/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py\nnew file mode 100755\nindex 00000000..be8985ce\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py\n@@ -0,0 +1,73 @@\n+#!/usr/bin/env python\n+# Copyright 2018 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+\"\"\"Simple utility which concatenates a set of files into a single output file\n+while also stripping any goog.provide or goog.require lines. This allows us to\n+provide a very primitive sort of \"compilation\" without any extra toolchain\n+support and without having to modify otherwise compilable sources in the tree\n+which use these directives.\n+\n+goog.provide lines are replaced with an equivalent invocation of\n+mojo.internal.exportModule, which accomplishes essentially the same thing in an\n+uncompiled context. A singular exception is made for the 'mojo.internal' export,\n+which is instead replaced with an inlined assignment to initialize the\n+namespace.\n+\"\"\"\n+\n+from __future__ import print_function\n+\n+import optparse\n+import re\n+\n+\n+_MOJO_INTERNAL_MODULE_NAME = \"mojo.internal\"\n+_MOJO_EXPORT_MODULE_SYMBOL = \"mojo.internal.exportModule\"\n+\n+\n+def FilterLine(filename, line, output):\n+  if line.startswith(\"goog.require\"):\n+    return\n+\n+  if line.startswith(\"goog.provide\"):\n+    match = re.match(\"goog.provide\\('([^']+)'\\);\", line)\n+    if not match:\n+      print(\"Invalid goog.provide line in %s:\\n%s\" % (filename, line))\n+      exit(1)\n+\n+    module_name = match.group(1)\n+    if module_name == _MOJO_INTERNAL_MODULE_NAME:\n+      output.write(\"self.mojo = { internal: {} };\")\n+    else:\n+      output.write(\"%s('%s');\\n\" % (_MOJO_EXPORT_MODULE_SYMBOL, module_name))\n+    return\n+\n+  output.write(line)\n+\n+def ConcatenateAndReplaceExports(filenames):\n+  if (len(filenames) < 2):\n+    print(\"At least two filenames (one input and the output) are required.\")\n+    return False\n+\n+  try:\n+    with open(filenames[-1], \"w\") as target:\n+      for filename in filenames[:-1]:\n+        with open(filename, \"r\") as current:\n+          for line in current.readlines():\n+            FilterLine(filename, line, target)\n+    return True\n+  except IOError as e:\n+    print(\"Error generating %s\\n: %s\" % (filenames[-1], e))\n+    return False\n+\n+def main():\n+  parser = optparse.OptionParser()\n+  parser.set_usage(\"\"\"file1 [file2...] outfile\n+    Concatenate several files into one, stripping Closure provide and\n+    require directives along the way.\"\"\")\n+  (_, args) = parser.parse_args()\n+  exit(0 if ConcatenateAndReplaceExports(args) else 1)\n+\n+if __name__ == \"__main__\":\n+  main()\ndiff --git a/utils/ipc/mojo/public/tools/bindings/format_typemap_generator_args.py b/utils/ipc/mojo/public/tools/bindings/format_typemap_generator_args.py\nnew file mode 100755\nindex 00000000..7ac4af5f\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/format_typemap_generator_args.py\n@@ -0,0 +1,36 @@\n+#!/usr/bin/env python\n+# Copyright 2016 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+from __future__ import print_function\n+\n+import sys\n+\n+# This utility converts mojom dependencies into their corresponding typemap\n+# paths and formats them to be consumed by generate_type_mappings.py.\n+\n+\n+def FormatTypemap(typemap_filename):\n+  # A simple typemap is valid Python with a minor alteration.\n+  with open(typemap_filename) as f:\n+    typemap_content = f.read().replace('=\\n', '=')\n+  typemap = {}\n+  exec typemap_content in typemap\n+\n+  for header in typemap.get('public_headers', []):\n+    yield 'public_headers=%s' % header\n+  for header in typemap.get('traits_headers', []):\n+    yield 'traits_headers=%s' % header\n+  for header in typemap.get('type_mappings', []):\n+    yield 'type_mappings=%s' % header\n+\n+\n+def main():\n+  typemaps = sys.argv[1:]\n+  print(' '.join('--start-typemap %s' % ' '.join(FormatTypemap(typemap))\n+                 for typemap in typemaps))\n+\n+\n+if __name__ == '__main__':\n+  sys.exit(main())\ndiff --git a/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py b/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py\nnew file mode 100644\nindex 00000000..79c9e50e\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py\n@@ -0,0 +1,52 @@\n+# Copyright 2017 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Generates a list of all files in a directory.\n+\n+This script takes in a directory and an output file name as input.\n+It then reads the directory and creates a list of all file names\n+in that directory.  The list is written to the output file.\n+There is also an option to pass in '-p' or '--pattern'\n+which will check each file name against a regular expression\n+pattern that is passed in.  Only files which match the regex\n+will be written to the list.\n+\"\"\"\n+\n+from __future__ import print_function\n+\n+import os\n+import re\n+import sys\n+\n+from cStringIO import StringIO\n+from optparse import OptionParser\n+\n+sys.path.insert(\n+    0,\n+    os.path.join(\n+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"mojom\"))\n+\n+from mojom.generate.generator import WriteFile\n+\n+\n+def main():\n+  parser = OptionParser()\n+  parser.add_option('-d', '--directory', help='Read files from DIRECTORY')\n+  parser.add_option('-o', '--output', help='Write list to FILE')\n+  parser.add_option('-p',\n+                    '--pattern',\n+                    help='Only reads files that name matches PATTERN',\n+                    default=\".\")\n+  (options, _) = parser.parse_args()\n+  pattern = re.compile(options.pattern)\n+  files = [f for f in os.listdir(options.directory) if pattern.match(f)]\n+\n+  stream = StringIO()\n+  for f in files:\n+    print(f, file=stream)\n+\n+  WriteFile(stream.getvalue(), options.output)\n+  stream.close()\n+\n+if __name__ == '__main__':\n+  sys.exit(main())\ndiff --git a/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py b/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py\nnew file mode 100755\nindex 00000000..64ca048f\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py\n@@ -0,0 +1,187 @@\n+#!/usr/bin/env python\n+# Copyright 2016 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Generates a JSON typemap from its command-line arguments and dependencies.\n+\n+Each typemap should be specified in an command-line argument of the form\n+key=value, with an argument of \"--start-typemap\" preceding each typemap.\n+\n+For example,\n+generate_type_mappings.py --output=foo.typemap --start-typemap \\\\\n+    public_headers=foo.h traits_headers=foo_traits.h \\\\\n+    type_mappings=mojom.Foo=FooImpl\n+\n+generates a foo.typemap containing\n+{\n+  \"c++\": {\n+    \"mojom.Foo\": {\n+      \"typename\": \"FooImpl\",\n+      \"traits_headers\": [\n+        \"foo_traits.h\"\n+      ],\n+      \"public_headers\": [\n+        \"foo.h\"\n+      ]\n+    }\n+  }\n+}\n+\n+Then,\n+generate_type_mappings.py --dependency foo.typemap --output=bar.typemap \\\\\n+    --start-typemap public_headers=bar.h traits_headers=bar_traits.h \\\\\n+    type_mappings=mojom.Bar=BarImpl\n+\n+generates a bar.typemap containing\n+{\n+  \"c++\": {\n+    \"mojom.Bar\": {\n+      \"typename\": \"BarImpl\",\n+      \"traits_headers\": [\n+        \"bar_traits.h\"\n+      ],\n+      \"public_headers\": [\n+        \"bar.h\"\n+      ]\n+    },\n+    \"mojom.Foo\": {\n+      \"typename\": \"FooImpl\",\n+      \"traits_headers\": [\n+        \"foo_traits.h\"\n+      ],\n+      \"public_headers\": [\n+        \"foo.h\"\n+      ]\n+    }\n+  }\n+}\n+\"\"\"\n+\n+import argparse\n+import json\n+import os\n+import re\n+import sys\n+\n+sys.path.insert(\n+    0,\n+    os.path.join(\n+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"mojom\"))\n+\n+from mojom.generate.generator import WriteFile\n+\n+def ReadTypemap(path):\n+  with open(path) as f:\n+    return json.load(f)['c++']\n+\n+\n+def ParseTypemapArgs(args):\n+  typemaps = [s for s in '\\n'.join(args).split('--start-typemap\\n') if s]\n+  result = {}\n+  for typemap in typemaps:\n+    result.update(ParseTypemap(typemap))\n+  return result\n+\n+\n+def LoadCppTypemapConfig(path):\n+  configs = {}\n+  with open(path) as f:\n+    for config in json.load(f):\n+      for entry in config['types']:\n+        configs[entry['mojom']] = {\n+            'typename': entry['cpp'],\n+            'public_headers': config.get('traits_headers', []),\n+            'traits_headers': config.get('traits_private_headers', []),\n+            'copyable_pass_by_value': entry.get('copyable_pass_by_value',\n+                                                False),\n+            'force_serialize': entry.get('force_serialize', False),\n+            'hashable': entry.get('hashable', False),\n+            'move_only': entry.get('move_only', False),\n+            'nullable_is_same_type': entry.get('nullable_is_same_type', False),\n+            'non_copyable_non_movable': False,\n+        }\n+  return configs\n+\n+\n+def ParseTypemap(typemap):\n+  values = {'type_mappings': [], 'public_headers': [], 'traits_headers': []}\n+  for line in typemap.split('\\n'):\n+    if not line:\n+      continue\n+    key, _, value = line.partition('=')\n+    values[key].append(value.lstrip('/'))\n+  result = {}\n+  mapping_pattern = \\\n+      re.compile(r\"\"\"^([^=]+)           # mojom type\n+                     =\n+                     ([^[]+)            # native type\n+                     (?:\\[([^]]+)\\])?$  # optional attribute in square brackets\n+                 \"\"\", re.X)\n+  for typename in values['type_mappings']:\n+    match_result = mapping_pattern.match(typename)\n+    assert match_result, (\n+        \"Cannot parse entry in the \\\"type_mappings\\\" section: %s\" % typename)\n+\n+    mojom_type = match_result.group(1)\n+    native_type = match_result.group(2)\n+    attributes = []\n+    if match_result.group(3):\n+      attributes = match_result.group(3).split(',')\n+\n+    assert mojom_type not in result, (\n+        \"Cannot map multiple native types (%s, %s) to the same mojom type: %s\" %\n+        (result[mojom_type]['typename'], native_type, mojom_type))\n+\n+    result[mojom_type] = {\n+        'public_headers': values['public_headers'],\n+        'traits_headers': values['traits_headers'],\n+        'typename': native_type,\n+\n+        # Attributes supported for individual mappings.\n+        'copyable_pass_by_value': 'copyable_pass_by_value' in attributes,\n+        'force_serialize': 'force_serialize' in attributes,\n+        'hashable': 'hashable' in attributes,\n+        'move_only': 'move_only' in attributes,\n+        'non_copyable_non_movable': 'non_copyable_non_movable' in attributes,\n+        'nullable_is_same_type': 'nullable_is_same_type' in attributes,\n+    }\n+  return result\n+\n+\n+def main():\n+  parser = argparse.ArgumentParser(\n+      description=__doc__,\n+      formatter_class=argparse.RawDescriptionHelpFormatter)\n+  parser.add_argument(\n+      '--dependency',\n+      type=str,\n+      action='append',\n+      default=[],\n+      help=('A path to another JSON typemap to merge into the output. '\n+            'This may be repeated to merge multiple typemaps.'))\n+  parser.add_argument(\n+      '--cpp-typemap-config',\n+      type=str,\n+      action='store',\n+      dest='cpp_config_path',\n+      help=('A path to a single JSON-formatted typemap config as emitted by'\n+            'GN when processing a mojom_cpp_typemap build rule.'))\n+  parser.add_argument('--output',\n+                      type=str,\n+                      required=True,\n+                      help='The path to which to write the generated JSON.')\n+  params, typemap_params = parser.parse_known_args()\n+  typemaps = ParseTypemapArgs(typemap_params)\n+  if params.cpp_config_path:\n+    typemaps.update(LoadCppTypemapConfig(params.cpp_config_path))\n+  missing = [path for path in params.dependency if not os.path.exists(path)]\n+  if missing:\n+    raise IOError('Missing dependencies: %s' % ', '.join(missing))\n+  for path in params.dependency:\n+    typemaps.update(ReadTypemap(path))\n+\n+  WriteFile(json.dumps({'c++': typemaps}, indent=2), params.output)\n+\n+\n+if __name__ == '__main__':\n+  main()\ndiff --git a/utils/ipc/mojo/public/tools/bindings/mojom.gni b/utils/ipc/mojo/public/tools/bindings/mojom.gni\nnew file mode 100644\nindex 00000000..a739fa6e\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/mojom.gni\n@@ -0,0 +1,1941 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import(\"//build/config/jumbo.gni\")\n+import(\"//third_party/closure_compiler/closure_args.gni\")\n+import(\"//third_party/closure_compiler/compile_js.gni\")\n+import(\"//third_party/protobuf/proto_library.gni\")\n+import(\"//ui/webui/webui_features.gni\")\n+\n+# TODO(rockot): Maybe we can factor these dependencies out of //mojo. They're\n+# used to conditionally enable message ID scrambling in a way which is\n+# consistent across toolchains and which is affected by branded vs non-branded\n+# Chrome builds. Ideally we could create some generic knobs here that could be\n+# flipped elsewhere though.\n+import(\"//build/config/chrome_build.gni\")\n+import(\"//build/config/chromecast_build.gni\")\n+import(\"//build/config/chromeos/ui_mode.gni\")\n+import(\"//build/config/nacl/config.gni\")\n+import(\"//build/toolchain/kythe.gni\")\n+import(\"//components/nacl/features.gni\")\n+import(\"//third_party/jinja2/jinja2.gni\")\n+import(\"//tools/ipc_fuzzer/ipc_fuzzer.gni\")\n+declare_args() {\n+  # Indicates whether typemapping should be supported in this build\n+  # configuration. This may be disabled when building external projects which\n+  # depend on //mojo but which do not need/want all of the Chromium tree\n+  # dependencies that come with typemapping.\n+  #\n+  # Note that (perhaps obviously) a huge amount of Chromium code will not build\n+  # with typemapping disabled, so it is never valid to set this to |false| in\n+  # any Chromium build configuration.\n+  enable_mojom_typemapping = true\n+\n+  # Controls message ID scrambling behavior. If |true|, message IDs are\n+  # scrambled (i.e. randomized based on the contents of //chrome/VERSION) on\n+  # non-Chrome OS desktop platforms. Set to |false| to disable message ID\n+  # scrambling on all platforms.\n+  enable_mojom_message_id_scrambling = true\n+\n+  # Enables Closure compilation of generated JS lite bindings. In environments\n+  # where compilation is supported, any mojom target \"foo\" will also have a\n+  # corresponding \"foo_js_library_for_compile\" target generated.\n+  enable_mojom_closure_compile = enable_js_type_check && optimize_webui\n+\n+  # Enables generating Typescript bindings and compiling them to JS bindings.\n+  enable_typescript_bindings = false\n+\n+  # Enables generating javascript fuzzing-related code and the bindings for the\n+  # MojoLPM fuzzer targets. Off by default.\n+  enable_mojom_fuzzer = false\n+}\n+\n+# NOTE: We would like to avoid scrambling message IDs where it doesn't add\n+# value, so we limit the behavior to desktop builds for now. There is some\n+# redundancy in the conditions here, but it is tolerated for clarity:\n+# We're explicit about Mac, Windows, and Linux desktop support, but it's\n+# also necessary to ensure that bindings in alternate toolchains (e.g.\n+# NaCl IRT) are always consistent with the default toolchain; for that\n+# reason we always enable scrambling within NaCl toolchains when possible,\n+# as well as within the default toolchain when NaCl is enabled.\n+#\n+# Finally, because we *cannot* enable scrambling on Chrome OS (it would break\n+# ARC) we have to explicitly opt out there even when NaCl is enabled (and\n+# consequently also when building for NaCl toolchains.) For this reason we\n+# check |target_os| explicitly, as it's consistent across all toolchains.\n+enable_scrambled_message_ids =\n+    enable_mojom_message_id_scrambling &&\n+    (is_mac || is_win || (is_linux && !is_chromeos && !is_chromecast &&\n+                          !chromeos_is_browser_only) ||\n+     ((enable_nacl || is_nacl || is_nacl_nonsfi) &&\n+      (target_os != \"chromeos\" && !chromeos_is_browser_only)))\n+\n+_mojom_tools_root = \"//mojo/public/tools\"\n+_mojom_library_root = \"$_mojom_tools_root/mojom/mojom\"\n+mojom_parser_script = \"$_mojom_tools_root/mojom/mojom_parser.py\"\n+mojom_parser_sources = [\n+  \"$_mojom_library_root/__init__.py\",\n+  \"$_mojom_library_root/error.py\",\n+  \"$_mojom_library_root/generate/__init__.py\",\n+  \"$_mojom_library_root/generate/constant_resolver.py\",\n+  \"$_mojom_library_root/generate/generator.py\",\n+  \"$_mojom_library_root/generate/module.py\",\n+  \"$_mojom_library_root/generate/pack.py\",\n+  \"$_mojom_library_root/generate/template_expander.py\",\n+  \"$_mojom_library_root/generate/translate.py\",\n+  \"$_mojom_library_root/parse/__init__.py\",\n+  \"$_mojom_library_root/parse/ast.py\",\n+  \"$_mojom_library_root/parse/lexer.py\",\n+  \"$_mojom_library_root/parse/parser.py\",\n+]\n+\n+mojom_generator_root = \"$_mojom_tools_root/bindings\"\n+mojom_generator_script = \"$mojom_generator_root/mojom_bindings_generator.py\"\n+mojom_generator_sources =\n+    mojom_parser_sources + [\n+      \"$mojom_generator_root/generators/mojom_cpp_generator.py\",\n+      \"$mojom_generator_root/generators/mojom_java_generator.py\",\n+      \"$mojom_generator_root/generators/mojom_mojolpm_generator.py\",\n+      \"$mojom_generator_root/generators/mojom_js_generator.py\",\n+      \"$mojom_generator_root/generators/mojom_ts_generator.py\",\n+      \"$mojom_generator_script\",\n+    ]\n+\n+if (enable_scrambled_message_ids) {\n+  declare_args() {\n+    # The path to a file whose contents can be used as the basis for a message\n+    # ID scrambling salt.\n+    mojom_message_id_salt_path = \"//chrome/VERSION\"\n+\n+    # The path to a file whose contents will be concatenated to the contents of\n+    # the file at |mojom_message_id_salt_path| to form a complete salt for\n+    # message ID scrambling. May be the empty string, in which case the contents\n+    # of the above file alone are used as the complete salt.\n+    if (is_chrome_branded) {\n+      mojom_message_id_salt_suffix_path =\n+          \"//mojo/internal/chrome-message-id-salt-suffix\"\n+    } else {\n+      mojom_message_id_salt_suffix_path = \"\"\n+    }\n+  }\n+\n+  assert(mojom_message_id_salt_path != \"\")\n+  message_scrambling_args = [\n+    \"--scrambled_message_id_salt_path\",\n+    rebase_path(mojom_message_id_salt_path, root_build_dir),\n+  ]\n+  message_scrambling_inputs = [ mojom_message_id_salt_path ]\n+\n+  if (mojom_message_id_salt_suffix_path != \"\") {\n+    message_scrambling_args += [\n+      \"--scrambled_message_id_salt_path\",\n+      rebase_path(mojom_message_id_salt_suffix_path, root_build_dir),\n+    ]\n+    message_scrambling_inputs += [ mojom_message_id_salt_suffix_path ]\n+  }\n+} else {\n+  message_scrambling_args = []\n+  message_scrambling_inputs = []\n+}\n+\n+if (enable_mojom_typemapping) {\n+  _bindings_configuration_files =\n+      [ \"//mojo/public/tools/bindings/chromium_bindings_configuration.gni\" ]\n+  _bindings_configurations = []\n+  foreach(config_file, _bindings_configuration_files) {\n+    _bindings_configurations += [ read_file(config_file, \"scope\") ]\n+  }\n+  foreach(configuration, _bindings_configurations) {\n+    # Check that the mojom field of each typemap refers to a mojom that exists.\n+    foreach(typemap, configuration.typemaps) {\n+      _typemap_config = {\n+      }\n+      _typemap_config = typemap.config\n+      read_file(_typemap_config.mojom, \"\")\n+    }\n+  }\n+} else {\n+  _bindings_configuration_files = []\n+  _bindings_configurations = [\n+    {\n+      typemaps = []\n+      component_macro_suffix = \"\"\n+    },\n+  ]\n+}\n+\n+if (!is_ios) {\n+  _bindings_configurations += [\n+    {\n+      variant = \"blink\"\n+      component_macro_suffix = \"_BLINK\"\n+      for_blink = true\n+      typemaps = []\n+    },\n+  ]\n+}\n+\n+# Generates targets for building C++, JavaScript and Java bindings from mojom\n+# files. The output files will go under the generated file directory tree with\n+# the same path as each input file.\n+#\n+# Other targets should depend on one of these generated targets (where \"foo\"\n+# is the target name):\n+#\n+#   foo\n+#       C++ bindings.\n+#\n+#   foo_blink\n+#       C++ bindings using Blink standard types.\n+#\n+#   foo_java\n+#       Java bindings.\n+#\n+#   foo_js\n+#       JavaScript bindings; used as compile-time dependency.\n+#\n+#   foo_js_data_deps\n+#       JavaScript bindings; used as run-time dependency.\n+#\n+# Parameters:\n+#\n+#   sources (optional if one of the deps sets listed below is present)\n+#       List of source .mojom files to compile.\n+#\n+#   deps (optional)\n+#       Note: this can contain only other mojom targets.\n+#\n+#       DEPRECATED: This is synonymous with public_deps because all mojom\n+#       dependencies must be public by design. Please use public_deps.\n+#\n+#   public_deps (optional)\n+#       Note: this can contain only other mojom targets.\n+#\n+#   parser_deps (optional)\n+#       List of non-mojom targets required for the mojom sources to be parsed.\n+#\n+#   import_dirs (optional)\n+#       List of import directories that will get added when processing sources.\n+#\n+#   testonly (optional)\n+#\n+#   visibility (optional)\n+#\n+#   visibility_blink (optional)\n+#       The value to use for visibility for the blink variant. If unset,\n+#       |visibility| is used.\n+#\n+#   cpp_only (optional)\n+#       If set to true, only the C++ bindings targets will be generated.\n+#\n+#       NOTE: If the global |enable_mojom_fuzzer| build arg is true, JS bindings\n+#       will still be generated even when |cpp_only| is set to |true|, unless\n+#       you also set |enable_fuzzing| to |false| in your mojom target.\n+#\n+#   cpp_typemaps (optional)\n+#       A list of typemaps to be applied to the generated C++ bindings for this\n+#       mojom target. Note that this only applies to the non-Blink variant of\n+#       generated C++ bindings.\n+#\n+#       Every typemap is a GN scope describing how one or more mojom types maps\n+#       to a non-mojom C++ type, including necessary deps and headers required\n+#       for the mapping to work. See the Typemaps section below.\n+#\n+#   blink_cpp_typemaps (optional)\n+#       Same as above, but for the Blink variant of generated C++ bindings.\n+#\n+#   generate_java (optional)\n+#       If set to true, Java bindings are generated for Android builds. If\n+#       |cpp_only| is set to true, it overrides this to prevent generation of\n+#       Java bindings.\n+#\n+#   enable_fuzzing (optional)\n+#       Enables generation of fuzzing sources for the target if the global build\n+#       arg |enable_mojom_fuzzer| is also set to |true|. Defaults to |true|. If\n+#       fuzzing generation is enabled for a target, the target will always\n+#       generate JS bindings even if |cpp_only| is set to |true|. See note\n+#       above.\n+#\n+#   support_lazy_serialization (optional)\n+#       If set to |true|, generated C++ bindings will effectively prefer to\n+#       transmit messages in an unserialized form when going between endpoints\n+#       in the same process. This avoids the runtime cost of serialization,\n+#       deserialization, and validation logic at the expensive of increased\n+#       code size. Defaults to |false|.\n+#\n+#   disable_variants (optional)\n+#       If |true|, no variant sources will be generated for the target. Defaults\n+#       to |false|.\n+#\n+#   disallow_native_types (optional)\n+#       If set to |true|, mojoms in this target may not apply the [Native]\n+#       attribute to struct or enum declarations. This avoids emitting code\n+#       which depends on legacy IPC serialization. Default is |false|, meaning\n+#       [Native] types are allowed.\n+#\n+#   disallow_interfaces (optional)\n+#       If set to |true|, mojoms in this target may not define interfaces.\n+#       Generates bindings with a smaller set of dependencies. Defaults to\n+#       |false|.\n+#\n+#   scramble_message_ids (optional)\n+#       If set to |true| (the default), generated mojom interfaces will use\n+#       scrambled ordinal identifiers in encoded messages.\n+#\n+#   component_output_prefix (optional)\n+#       The prefix to use for the output_name of any component library emitted\n+#       for generated C++ bindings. If this is omitted, C++ bindings targets are\n+#       emitted as source_sets instead. Because this controls the name of the\n+#       output shared library binary in the root output directory, it must be\n+#       unique across the entire build configuration.\n+#\n+#       This is required if |component_macro_prefix| is specified.\n+#\n+#   component_macro_prefix (optional)\n+#       This specifies a macro prefix to use for component export macros and\n+#       should therefore be globally unique in the project. For example if this\n+#       is \"FOO_BAR\", then the generated C++ sources will be built with\n+#       IS_FOO_BAR_{suffix}_IMPL defined, and the generated public headers will\n+#       annotate public symbol definitions with\n+#       COMPONENT_EXPORT(FOO_BAR_{suffix}). \"suffix\" in this case depends on\n+#       which internal subtarget is generating the code (e.g. \"SHARED\", or a\n+#       variant name like \"BLINK\").\n+#\n+#   enabled_features (optional)\n+#       Definitions in a mojom file can be guarded by an EnableIf attribute. If\n+#       the value specified by the attribute does not match any items in the\n+#       list of enabled_features, the definition will be disabled, with no code\n+#       emitted for it.\n+#\n+#   generate_closure_exports (optional)\n+#       Generates JS lite bindings will use goog.provide and goog.require\n+#       annotations to export its symbols and import core Mojo bindings support\n+#       and other mojom dependency modules. Use this if you plan to compile your\n+#       bindings into a larger JS binary. Defaults to |false|, instead\n+#       generating JS lite bindings which assume they will be manually loaded in\n+#       correct dependency order. Note that this only has an effect if\n+#       the |enable_mojom_closure_compile| global arg is set to |true| as well.\n+#\n+#   use_typescript_sources (optional)\n+#       Uses the Typescript generator to generate JavaScript bindings.\n+#\n+#   js_generate_struct_deserializers (optional)\n+#       Generates JS deerialize methods for structs.\n+#\n+#   extra_cpp_template_paths (optional)\n+#       List of extra C++ templates that are used to generate additional source\n+#       and/or header files. The templates should end with extension \".tmpl\".\n+#\n+# The following parameters are used to support the component build. They are\n+# needed so that bindings which are linked with a component can use the same\n+# export settings for classes. The first three are for the chromium variant, and\n+# the last three are for the blink variant. These parameters can also override\n+# |component_macro_prefix| for a specific variant, allowing e.g. one variant\n+# to be linked into a larger non-mojom component target, while all other\n+# variants get their own unique component target.\n+#   export_class_attribute (optional)\n+#       The attribute to add to the class declaration. e.g. \"CONTENT_EXPORT\"\n+#   export_define (optional)\n+#       A define to be added to the source_set which is needed by the export\n+#       header. e.g. \"CONTENT_IMPLEMENTATION=1\"\n+#   export_header (optional)\n+#       A header to be added to the generated bindings to support the component\n+#       build. e.g. \"content/common/content_export.h\"\n+#   export_class_attribute_blink (optional)\n+#   export_define_blink (optional)\n+#   export_header_blink (optional)\n+#       These three parameters are the blink variants of the previous 3.\n+#\n+# The following parameters are used to correct component build dependencies.\n+# They are needed so mojom-mojom dependencies follow the rule that dependencies\n+# on a source set in another component are replaced by a dependency on the\n+# containing component. The first two are for the chromium variant; the other\n+# two are for the blink variant.\n+#   overridden_deps (optional)\n+#       The list of mojom deps to be overridden.\n+#   component_deps (optional)\n+#       The list of component deps to add to replace overridden_deps.\n+#   overridden_deps_blink (optional)\n+#   component_deps_blink (optional)\n+#       These two parameters are the blink variants of the previous two.\n+#\n+#   check_includes_blink (optional)\n+#       Overrides the check_includes variable for the blink variant.\n+#       If check_includes_blink is not defined, the check_includes variable\n+#       retains its original value.\n+#\n+# Typemaps\n+# ========\n+# The cpp_typemaps and blink_cpp_typemaps each specify an optional list of\n+# typemapping configurations. Each configuration is a GN scope with metadata\n+# describing what and how to map.\n+#\n+# Typemap scope parameters:\n+#   types\n+#       A list of type specifications for this typemap. Each type specification\n+#       is a nested GN scope which can be expressed with the following syntax:\n+#\n+#           {\n+#             mojom = \"foo.mojom.Bar\"\n+#             cpp = \"::foo::LegitBar\"\n+#             move_only = true\n+#             # etc...\n+#           }\n+#\n+#       Each type specification supports the following values:\n+#\n+#         mojom (required)\n+#             The fully qualified name of a mojom type to be mapped. This is a\n+#             string like \"foo.mojom.Bar\".\n+#\n+#         cpp (required)\n+#             The fully qualified name of the C++ type to which the mojom type\n+#             should be mapped in generated bindings. This is a string like\n+#             \"::base::Value\" or \"std::vector<::base::Value>\".\n+#\n+#         move_only (optional)\n+#             A boolean value (default false) which indicates whether the C++\n+#             type is move-only. If true, generated bindings will pass the type\n+#             by value and use std::move() at call sites.\n+#\n+#         copyable_pass_by_value (optional)\n+#             A boolean value (default false) which effectively indicates\n+#             whether the C++ type is very cheap to copy. If so, generated\n+#             bindings will pass by value but not use std::move() at call sites.\n+#\n+#         nullable_is_same_type (optional)\n+#             A boolean value (default false) which indicates that the C++ type\n+#             has some baked-in semantic notion of a \"null\" state. If true, the\n+#             traits for the type must define IsNull and SetToNull methods.\n+#\n+#             When false, nullable fields are represented by wrapping the C++\n+#             type with base::Optional, and null values are simply\n+#             base::nullopt.\n+#\n+#         hashable (optional)\n+#             A boolean value (default false) indicating whether the C++ type is\n+#             hashable. Set to true if true AND needed (i.e. you need to use the\n+#             type as the key of a mojom map).\n+#\n+#         force_serialize (optional)\n+#             A boolean value (default false) which disables lazy serialization\n+#             of the typemapped type if lazy serialization is enabled for the\n+#             mojom target applying this typemap.\n+#\n+# Additional typemap scope parameters:\n+#\n+#   traits_headers (optional)\n+#       Headers which must be included in the generated mojom in order for\n+#       serialization to be possible. This generally means including at least\n+#       the header for the corresponding mojom traits definitions.\n+#\n+#   traits_private_headers (optional)\n+#       Headers which must be included in generated C++ serialization code for\n+#       a mojom using the typemap. This should be used only when including a\n+#       header in |traits_headers| is problematic for compilation, as is\n+#       sometimes the case with legacy IPC message headers.\n+#\n+#   traits_sources (optional)\n+#       The references to the source files (typically a single .cc and .h file)\n+#       defining an appropriate set of EnumTraits or StructTraits, etc for the\n+#       the type-mapping. Using this will cause the listed sources to be\n+#       integrated directly into the dependent mojom's generated type-mapping\n+#       targets.\n+#\n+#       Prefer using |traits_public_deps| over inlined |traits_sources|, as this\n+#       will generally lead to easier build maintenance over time.\n+#\n+#       NOTE: If a typemap is shared by Blink and non-Blink bindings, you cannot\n+#       use this and MUST use |traits_public_deps| to reference traits built\n+#       within a separate target.\n+#\n+#   traits_deps / traits_public_deps (optional)\n+#       Any dependencies of sources in |traits_headers| or |traits_sources| must\n+#       be listed here.\n+#\n+template(\"mojom\") {\n+  assert(\n+      defined(invoker.sources) || defined(invoker.deps) ||\n+          defined(invoker.public_deps),\n+      \"\\\"sources\\\" or \\\"deps\\\" must be defined for the $target_name template.\")\n+\n+  if (defined(invoker.export_class_attribute) ||\n+      defined(invoker.export_define) || defined(invoker.export_header)) {\n+    assert(defined(invoker.export_class_attribute))\n+    assert(defined(invoker.export_define))\n+    assert(defined(invoker.export_header))\n+  }\n+  if (defined(invoker.export_class_attribute_blink) ||\n+      defined(invoker.export_define_blink) ||\n+      defined(invoker.export_header_blink)) {\n+    assert(defined(invoker.export_class_attribute_blink))\n+    assert(defined(invoker.export_define_blink))\n+    assert(defined(invoker.export_header_blink))\n+\n+    # Not all platforms use the Blink variant, so make sure GN doesn't complain\n+    # about these values being inconsequential.\n+    not_needed(invoker,\n+               [\n+                 \"export_class_attribute_blink\",\n+                 \"export_define_blink\",\n+                 \"export_header_blink\",\n+               ])\n+  }\n+  if (defined(invoker.overridden_deps) || defined(invoker.component_deps)) {\n+    assert(defined(invoker.overridden_deps))\n+    assert(defined(invoker.component_deps))\n+  }\n+\n+  if (defined(invoker.overridden_deps_blink) ||\n+      defined(invoker.component_deps_blink)) {\n+    assert(defined(invoker.overridden_deps_blink))\n+    assert(defined(invoker.component_deps_blink))\n+  }\n+\n+  # Type-mapping may be disabled or we may not generate C++ bindings.\n+  not_needed(invoker,\n+             [\n+               \"cpp_typemaps\",\n+               \"blink_cpp_typemaps\",\n+             ])\n+\n+  require_full_cpp_deps =\n+      !defined(invoker.disallow_native_types) ||\n+      !invoker.disallow_native_types || !defined(invoker.disallow_interfaces) ||\n+      !invoker.disallow_interfaces\n+\n+  all_deps = []\n+  if (defined(invoker.deps)) {\n+    all_deps += invoker.deps\n+  }\n+  if (defined(invoker.public_deps)) {\n+    all_deps += invoker.public_deps\n+  }\n+\n+  if (defined(invoker.component_macro_prefix)) {\n+    assert(defined(invoker.component_output_prefix))\n+  }\n+\n+  group(\"${target_name}__is_mojom\") {\n+  }\n+\n+  # Explicitly ensure that all dependencies (invoker.deps and\n+  # invoker.public_deps) are mojom targets.\n+  group(\"${target_name}__check_deps_are_all_mojom\") {\n+    deps = []\n+    foreach(d, all_deps) {\n+      name = get_label_info(d, \"label_no_toolchain\")\n+      toolchain = get_label_info(d, \"toolchain\")\n+      deps += [ \"${name}__is_mojom(${toolchain})\" ]\n+    }\n+  }\n+\n+  sources_list = []\n+  if (defined(invoker.sources)) {\n+    sources_list = invoker.sources\n+  }\n+\n+  # Reset sources_assignment_filter for the BUILD.gn file to prevent\n+  # regression during the migration of Chromium away from the feature.\n+  # See docs/no_sources_assignment_filter.md for more information.\n+  # TODO(crbug.com/1018739): remove this when migration is done.\n+  set_sources_assignment_filter([])\n+\n+  # Listed sources may be relative to the current target dir, or they may be\n+  # absolute paths, including paths to generated mojom files. While those are\n+  # fine as-is for input references, deriving output paths can be more subtle.\n+  #\n+  # Here we rewrite all source paths to be relative to the root build dir and\n+  # strip any root_gen_dir prefixes.\n+  #\n+  # So for a target in //foo/bar with:\n+  #\n+  #     sources = [\n+  #       \"a.mojom\",\n+  #       \"b/c.mojom\",\n+  #       \"//baz/d.mojom\",\n+  #       \"$target_gen_dir/e.mojom\",\n+  #     ]\n+  #\n+  # output_file_base_paths will be:\n+  #\n+  #     [\n+  #       \"foo/bar/a.mojom\",\n+  #       \"foo/bar/b/c.mojom\",\n+  #       \"baz/d.mojom\",\n+  #       \"foo/bar/e.mojom\",\n+  #     ]\n+  #\n+  # This result is essentially a list of base filename paths which are suitable\n+  # for the naming of any generated output files derived from their\n+  # corresponding input mojoms. These paths are always considered to be relative\n+  # to root_gen_dir.\n+  source_abspaths = rebase_path(sources_list, \"//\")\n+  output_file_base_paths = []\n+  foreach(path, source_abspaths) {\n+    output_file_base_paths +=\n+        [ string_replace(path, rebase_path(root_gen_dir, \"//\") + \"/\", \"\") ]\n+  }\n+\n+  # Sanity check that either all input files have a .mojom extension, or\n+  # all input files have a .test-mojom extension AND |testonly| is |true|.\n+  sources_list_filenames =\n+      process_file_template(sources_list, \"{{source_file_part}}\")\n+  sources_list_filenames_with_mojom_extension =\n+      process_file_template(sources_list, \"{{source_name_part}}.mojom\")\n+  if (sources_list_filenames != sources_list_filenames_with_mojom_extension) {\n+    sources_list_filenames_with_test_mojom_extension =\n+        process_file_template(sources_list, \"{{source_name_part}}.test-mojom\")\n+    if (sources_list_filenames ==\n+        sources_list_filenames_with_test_mojom_extension) {\n+      assert(\n+          defined(invoker.testonly) && invoker.testonly,\n+          \"mojom targets for .test-mojom files must set |testonly| to |true|\")\n+    } else {\n+      assert(\n+          false,\n+          \"One or more mojom files has an invalid extension. The only \" +\n+              \"allowed extensions are .mojom and .test-mojom, and any given \" +\n+              \"mojom target must use one or the other exclusively.\")\n+    }\n+  }\n+\n+  build_metadata_filename = \"$target_gen_dir/$target_name.build_metadata\"\n+  build_metadata = {\n+  }\n+  build_metadata.sources = rebase_path(sources_list)\n+  build_metadata.deps = []\n+  foreach(dep, all_deps) {\n+    dep_target_gen_dir = get_label_info(dep, \"target_gen_dir\")\n+    dep_name = get_label_info(dep, \"name\")\n+    build_metadata.deps +=\n+        [ rebase_path(\"$dep_target_gen_dir/$dep_name.build_metadata\") ]\n+  }\n+  write_file(build_metadata_filename, build_metadata, \"json\")\n+\n+  generate_fuzzing =\n+      (!defined(invoker.enable_fuzzing) || invoker.enable_fuzzing) &&\n+      enable_mojom_fuzzer && (!defined(invoker.testonly) || !invoker.testonly)\n+\n+  parser_target_name = \"${target_name}__parser\"\n+  parser_deps = []\n+  foreach(dep, all_deps) {\n+    _label = get_label_info(dep, \"label_no_toolchain\")\n+    parser_deps += [ \"${_label}__parser\" ]\n+  }\n+  if (defined(invoker.parser_deps)) {\n+    parser_deps += invoker.parser_deps\n+  }\n+  if (sources_list == []) {\n+    # Even without sources we generate a parser target to at least forward\n+    # other parser dependencies.\n+    group(parser_target_name) {\n+      public_deps = parser_deps\n+    }\n+  } else {\n+    enabled_features = []\n+    if (defined(invoker.enabled_features)) {\n+      enabled_features += invoker.enabled_features\n+    }\n+    if (is_posix) {\n+      enabled_features += [ \"is_posix\" ]\n+    }\n+    if (is_android) {\n+      enabled_features += [ \"is_android\" ]\n+    } else if (is_chromeos) {\n+      enabled_features += [ \"is_chromeos\" ]\n+    } else if (is_fuchsia) {\n+      enabled_features += [ \"is_fuchsia\" ]\n+    } else if (is_ios) {\n+      enabled_features += [ \"is_ios\" ]\n+    } else if (is_linux) {\n+      enabled_features += [ \"is_linux\" ]\n+    } else if (is_mac) {\n+      enabled_features += [ \"is_mac\" ]\n+    } else if (is_win) {\n+      enabled_features += [ \"is_win\" ]\n+    }\n+\n+    action(parser_target_name) {\n+      script = mojom_parser_script\n+      inputs = mojom_parser_sources + [ build_metadata_filename ]\n+      sources = sources_list\n+      deps = parser_deps\n+      outputs = []\n+      foreach(base_path, output_file_base_paths) {\n+        filename = get_path_info(base_path, \"file\")\n+        dirname = get_path_info(base_path, \"dir\")\n+        outputs += [ \"$root_gen_dir/$dirname/${filename}-module\" ]\n+      }\n+\n+      filelist = []\n+      foreach(source, sources_list) {\n+        filelist += [ rebase_path(source) ]\n+      }\n+      response_file_contents = filelist\n+\n+      args = [\n+        # Resolve relative input mojom paths against both the root src dir and\n+        # the root gen dir.\n+        \"--input-root\",\n+        rebase_path(\"//\"),\n+        \"--input-root\",\n+        rebase_path(root_gen_dir),\n+\n+        \"--output-root\",\n+        rebase_path(root_gen_dir),\n+\n+        \"--mojom-file-list={{response_file_name}}\",\n+\n+        \"--check-imports\",\n+        rebase_path(build_metadata_filename),\n+      ]\n+\n+      foreach(enabled_feature, enabled_features) {\n+        args += [\n+          \"--enable-feature\",\n+          enabled_feature,\n+        ]\n+      }\n+    }\n+  }\n+\n+  generator_cpp_message_ids_target_name = \"${target_name}__generate_message_ids\"\n+\n+  # Generate code that is shared by different variants.\n+  if (sources_list != []) {\n+    common_generator_args = [\n+      \"--use_bundled_pylibs\",\n+      \"-o\",\n+      rebase_path(root_gen_dir, root_build_dir),\n+      \"generate\",\n+      \"-d\",\n+      rebase_path(\"//\", root_build_dir),\n+      \"-I\",\n+      rebase_path(\"//\", root_build_dir),\n+      \"--bytecode_path\",\n+      rebase_path(\"$root_gen_dir/mojo/public/tools/bindings\", root_build_dir),\n+    ]\n+\n+    if (defined(invoker.disallow_native_types) &&\n+        invoker.disallow_native_types) {\n+      common_generator_args += [ \"--disallow_native_types\" ]\n+    }\n+\n+    if (defined(invoker.disallow_interfaces) && invoker.disallow_interfaces) {\n+      common_generator_args += [ \"--disallow_interfaces\" ]\n+    }\n+\n+    if (defined(invoker.import_dirs)) {\n+      foreach(import_dir, invoker.import_dirs) {\n+        common_generator_args += [\n+          \"-I\",\n+          rebase_path(import_dir, root_build_dir),\n+        ]\n+      }\n+    }\n+\n+    if (defined(invoker.component_macro_prefix)) {\n+      shared_component_export_macro =\n+          \"COMPONENT_EXPORT(${invoker.component_macro_prefix}_SHARED)\"\n+      shared_component_impl_macro =\n+          \"IS_${invoker.component_macro_prefix}_SHARED_IMPL\"\n+      shared_component_output_name = \"${invoker.component_output_prefix}_shared\"\n+    } else if (defined(invoker.export_class_attribute_shared) ||\n+               defined(invoker.export_class_attribute)) {\n+      if (defined(invoker.export_class_attribute_shared)) {\n+        assert(defined(invoker.export_header_shared))\n+        shared_component_export_macro = invoker.export_class_attribute_shared\n+        shared_component_impl_macro = invoker.export_define_shared\n+      } else {\n+        assert(!defined(invoker.export_header_shared))\n+\n+        # If no explicit shared attribute/define was provided by the invoker,\n+        # we derive some reasonable settings frorm the default variant.\n+        shared_component_export_macro = \"COMPONENT_EXPORT(MOJOM_SHARED_\" +\n+                                        invoker.export_class_attribute + \")\"\n+        shared_component_impl_macro =\n+            \"IS_MOJOM_SHARED_\" + invoker.export_class_attribute + \"_IMPL\"\n+      }\n+\n+      if (defined(invoker.component_output_prefix)) {\n+        shared_component_output_name =\n+            \"${invoker.component_output_prefix}_shared\"\n+      } else {\n+        shared_component_output_name = \"${target_name}_shared\"\n+      }\n+    }\n+\n+    action(generator_cpp_message_ids_target_name) {\n+      script = mojom_generator_script\n+      inputs = mojom_generator_sources + jinja2_sources\n+      sources = sources_list\n+      deps = [\n+        \":$parser_target_name\",\n+        \"//mojo/public/tools/bindings:precompile_templates\",\n+      ]\n+      if (defined(invoker.parser_deps)) {\n+        deps += invoker.parser_deps\n+      }\n+      outputs = []\n+      args = common_generator_args\n+      filelist = []\n+      foreach(source, sources_list) {\n+        filelist += [ rebase_path(\"$source\", root_build_dir) ]\n+      }\n+      foreach(base_path, output_file_base_paths) {\n+        outputs += [ \"$root_gen_dir/$base_path-shared-message-ids.h\" ]\n+      }\n+\n+      response_file_contents = filelist\n+\n+      args += [\n+        \"--filelist={{response_file_name}}\",\n+        \"--generate_non_variant_code\",\n+        \"--generate_message_ids\",\n+        \"-g\",\n+        \"c++\",\n+      ]\n+\n+      if (!defined(invoker.scramble_message_ids) ||\n+          invoker.scramble_message_ids) {\n+        inputs += message_scrambling_inputs\n+        args += message_scrambling_args\n+      }\n+    }\n+\n+    generator_shared_target_name = \"${target_name}_shared__generator\"\n+    action(generator_shared_target_name) {\n+      visibility = [ \":*\" ]\n+      script = mojom_generator_script\n+      inputs = mojom_generator_sources + jinja2_sources\n+      sources = sources_list\n+      deps = [\n+        \":$parser_target_name\",\n+        \"//mojo/public/tools/bindings:precompile_templates\",\n+      ]\n+      if (defined(invoker.parser_deps)) {\n+        deps += invoker.parser_deps\n+      }\n+\n+      outputs = []\n+      args = common_generator_args\n+      filelist = []\n+      foreach(source, sources_list) {\n+        filelist += [ rebase_path(\"$source\", root_build_dir) ]\n+      }\n+      foreach(base_path, output_file_base_paths) {\n+        outputs += [\n+          \"$root_gen_dir/$base_path-params-data.h\",\n+          \"$root_gen_dir/$base_path-shared-internal.h\",\n+          \"$root_gen_dir/$base_path-shared.cc\",\n+          \"$root_gen_dir/$base_path-shared.h\",\n+        ]\n+      }\n+\n+      response_file_contents = filelist\n+\n+      args += [\n+        \"--filelist={{response_file_name}}\",\n+        \"--generate_non_variant_code\",\n+        \"-g\",\n+        \"c++\",\n+      ]\n+\n+      if (defined(shared_component_export_macro)) {\n+        args += [\n+          \"--export_attribute\",\n+          shared_component_export_macro,\n+          \"--export_header\",\n+          \"base/component_export.h\",\n+        ]\n+      }\n+\n+      # Enable adding annotations to generated C++ headers that are used for\n+      # cross-references in CodeSearch.\n+      if (enable_kythe_annotations) {\n+        args += [ \"--enable_kythe_annotations\" ]\n+      }\n+    }\n+  } else {\n+    group(generator_cpp_message_ids_target_name) {\n+    }\n+  }\n+\n+  shared_cpp_sources_target_name = \"${target_name}_shared_cpp_sources\"\n+  jumbo_source_set(shared_cpp_sources_target_name) {\n+    if (defined(invoker.testonly)) {\n+      testonly = invoker.testonly\n+    }\n+    deps = []\n+    public_deps = []\n+    if (output_file_base_paths != []) {\n+      sources = []\n+      foreach(base_path, output_file_base_paths) {\n+        sources += [\n+          \"$root_gen_dir/$base_path-params-data.h\",\n+          \"$root_gen_dir/$base_path-shared-internal.h\",\n+          \"$root_gen_dir/$base_path-shared.cc\",\n+          \"$root_gen_dir/$base_path-shared.h\",\n+        ]\n+      }\n+      public_deps += [ \":$generator_shared_target_name\" ]\n+    }\n+    if (require_full_cpp_deps) {\n+      public_deps += [ \"//mojo/public/cpp/bindings\" ]\n+    } else {\n+      public_deps += [ \"//mojo/public/cpp/bindings:bindings_base\" ]\n+    }\n+    foreach(d, all_deps) {\n+      # Resolve the name, so that a target //mojo/something becomes\n+      # //mojo/something:something and we can append shared_cpp_sources_suffix\n+      # to get the cpp dependency name.\n+      full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+      public_deps += [ \"${full_name}_shared\" ]\n+    }\n+    if (defined(shared_component_impl_macro)) {\n+      defines = [ shared_component_impl_macro ]\n+    }\n+  }\n+\n+  shared_cpp_library_target_name = \"${target_name}_shared\"\n+  if (defined(shared_component_output_name)) {\n+    component(shared_cpp_library_target_name) {\n+      if (defined(invoker.testonly)) {\n+        testonly = invoker.testonly\n+      }\n+      output_name = \"$shared_component_output_name\"\n+      public_deps = [ \":$shared_cpp_sources_target_name\" ]\n+    }\n+  } else {\n+    group(shared_cpp_library_target_name) {\n+      if (defined(invoker.testonly)) {\n+        testonly = invoker.testonly\n+      }\n+      public_deps = [ \":$shared_cpp_sources_target_name\" ]\n+    }\n+  }\n+\n+  if (generate_fuzzing) {\n+    # This block generates the proto files used for the MojoLPM fuzzer,\n+    # and the corresponding proto targets that will be linked in the fuzzer\n+    # targets. These are independent of the typemappings, and can be done\n+    # separately here.\n+\n+    generator_mojolpm_proto_target_name =\n+        \"${target_name}_mojolpm_proto_generator\"\n+    action(generator_mojolpm_proto_target_name) {\n+      script = mojom_generator_script\n+      inputs = mojom_generator_sources + jinja2_sources\n+      sources = invoker.sources\n+      deps = [\n+        \":$parser_target_name\",\n+        \"//mojo/public/tools/bindings:precompile_templates\",\n+      ]\n+\n+      outputs = []\n+      args = common_generator_args\n+      filelist = []\n+      foreach(source, invoker.sources) {\n+        filelist += [ rebase_path(\"$source\", root_build_dir) ]\n+        outputs += [ \"$target_gen_dir/$source.mojolpm.proto\" ]\n+      }\n+\n+      response_file_contents = filelist\n+\n+      args += [\n+        \"--filelist={{response_file_name}}\",\n+        \"--generate_non_variant_code\",\n+        \"-g\",\n+        \"mojolpm\",\n+      ]\n+    }\n+\n+    mojolpm_proto_target_name = \"${target_name}_mojolpm_proto\"\n+    if (defined(invoker.sources)) {\n+      proto_library(mojolpm_proto_target_name) {\n+        testonly = true\n+        generate_python = false\n+        sources = process_file_template(\n+                invoker.sources,\n+                [ \"{{source_gen_dir}}/{{source_file_part}}.mojolpm.proto\" ])\n+        import_dirs = [ \"${root_gen_dir}\" ]\n+        proto_in_dir = \"${root_gen_dir}\"\n+        proto_out_dir = \".\"\n+        proto_deps = [ \"//mojo/public/tools/fuzzers:mojolpm_proto_copy\" ]\n+        proto_deps += [ \":$generator_mojolpm_proto_target_name\" ]\n+        link_deps = [ \"//mojo/public/tools/fuzzers:mojolpm_proto\" ]\n+\n+        foreach(d, all_deps) {\n+          # Resolve the name, so that a target //mojo/something becomes\n+          # //mojo/something:something and we can append mojolpm_proto_suffix\n+          # to get the proto dependency name.\n+          full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+          proto_deps += [ \"${full_name}_mojolpm_proto\" ]\n+          link_deps += [ \"${full_name}_mojolpm_proto\" ]\n+        }\n+      }\n+    } else {\n+      group(mojolpm_proto_target_name) {\n+        testonly = true\n+        public_deps = [ \"//mojo/public/tools/fuzzers:mojolpm_proto\" ]\n+        if (defined(generator_shared_target_name)) {\n+          public_deps += [ \":$generator_shared_target_name\" ]\n+        }\n+        foreach(d, all_deps) {\n+          # Resolve the name, so that a target //mojo/something becomes\n+          # //mojo/something:something and we can append #mojolpm_proto_suffix\n+          # to get the proto dependency name.\n+          full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+          public_deps += [ \"${full_name}_mojolpm_proto\" ]\n+        }\n+      }\n+    }\n+  }\n+\n+  # Generate code for variants.\n+  if (!defined(invoker.disable_variants) || !invoker.disable_variants) {\n+    enabled_configurations = _bindings_configurations\n+  } else {\n+    first_config = _bindings_configurations[0]\n+    assert(!defined(first_config.variant))\n+    enabled_configurations = [ first_config ]\n+  }\n+  foreach(bindings_configuration, enabled_configurations) {\n+    cpp_only = false\n+    if (defined(invoker.cpp_only)) {\n+      cpp_only = invoker.cpp_only\n+    }\n+    variant_suffix = \"\"\n+    if (defined(bindings_configuration.variant)) {\n+      variant = bindings_configuration.variant\n+      variant_suffix = \"_${variant}\"\n+      cpp_only = true\n+    }\n+\n+    cpp_typemap_configs = []\n+    export_defines = []\n+    export_defines_overridden = false\n+    force_source_set = false\n+    if (defined(bindings_configuration.for_blink) &&\n+        bindings_configuration.for_blink) {\n+      if (defined(invoker.blink_cpp_typemaps)) {\n+        cpp_typemap_configs = invoker.blink_cpp_typemaps\n+      }\n+      if (defined(invoker.export_define_blink)) {\n+        export_defines_overridden = true\n+        export_defines = [ invoker.export_define_blink ]\n+        force_source_set = true\n+      }\n+    } else {\n+      if (defined(invoker.cpp_typemaps)) {\n+        cpp_typemap_configs = invoker.cpp_typemaps\n+      }\n+\n+      if (defined(invoker.export_define)) {\n+        export_defines_overridden = true\n+        export_defines = [ invoker.export_define ]\n+        force_source_set = true\n+      }\n+    }\n+    not_needed([ \"cpp_typemap_configs\" ])\n+\n+    if (!export_defines_overridden && defined(invoker.component_macro_prefix)) {\n+      output_name_override =\n+          \"${invoker.component_output_prefix}${variant_suffix}\"\n+      export_defines =\n+          [ \"IS_${invoker.component_macro_prefix}\" +\n+            \"${bindings_configuration.component_macro_suffix}_IMPL\" ]\n+    }\n+\n+    export_args = []\n+    export_args_overridden = false\n+    if (defined(bindings_configuration.for_blink) &&\n+        bindings_configuration.for_blink) {\n+      if (defined(invoker.export_class_attribute_blink)) {\n+        export_args_overridden = true\n+        export_args += [\n+          \"--export_attribute\",\n+          invoker.export_class_attribute_blink,\n+          \"--export_header\",\n+          invoker.export_header_blink,\n+        ]\n+      }\n+    } else if (defined(invoker.export_class_attribute)) {\n+      export_args_overridden = true\n+      export_args += [\n+        \"--export_attribute\",\n+        invoker.export_class_attribute,\n+        \"--export_header\",\n+        invoker.export_header,\n+      ]\n+    }\n+\n+    if (!export_args_overridden && defined(invoker.component_macro_prefix)) {\n+      export_args += [\n+        \"--export_attribute\",\n+        \"COMPONENT_EXPORT(${invoker.component_macro_prefix}\" +\n+            \"${bindings_configuration.component_macro_suffix})\",\n+        \"--export_header\",\n+        \"base/component_export.h\",\n+      ]\n+    }\n+\n+    generate_java = false\n+    if (!cpp_only && defined(invoker.generate_java)) {\n+      generate_java = invoker.generate_java\n+    }\n+    type_mappings_target_name = \"${target_name}${variant_suffix}__type_mappings\"\n+    type_mappings_path =\n+        \"$target_gen_dir/${target_name}${variant_suffix}__type_mappings\"\n+    active_typemaps = []\n+    if (sources_list != []) {\n+      generator_cpp_output_suffixes = []\n+      variant_dash_suffix = \"\"\n+      if (defined(variant)) {\n+        variant_dash_suffix = \"-${variant}\"\n+      }\n+      generator_cpp_output_suffixes += [\n+        \"${variant_dash_suffix}-forward.h\",\n+        \"${variant_dash_suffix}-import-headers.h\",\n+        \"${variant_dash_suffix}-test-utils.cc\",\n+        \"${variant_dash_suffix}-test-utils.h\",\n+        \"${variant_dash_suffix}.cc\",\n+        \"${variant_dash_suffix}.h\",\n+      ]\n+      foreach(source, sources_list) {\n+        # TODO(sammc): Use a map instead of a linear scan when GN supports maps.\n+        foreach(typemap, bindings_configuration.typemaps) {\n+          _typemap_config = {\n+          }\n+          _typemap_config = typemap.config\n+          if (get_path_info(source, \"abspath\") == _typemap_config.mojom) {\n+            active_typemaps += [ typemap ]\n+          }\n+        }\n+      }\n+\n+      generator_target_name = \"${target_name}${variant_suffix}__generator\"\n+      action(generator_target_name) {\n+        visibility = [ \":*\" ]\n+        script = mojom_generator_script\n+        inputs = mojom_generator_sources + jinja2_sources\n+        sources = sources_list\n+        deps = [\n+          \":$parser_target_name\",\n+          \":$type_mappings_target_name\",\n+          \"//mojo/public/tools/bindings:precompile_templates\",\n+        ]\n+        if (defined(invoker.parser_deps)) {\n+          deps += invoker.parser_deps\n+        }\n+        outputs = []\n+        args = common_generator_args + export_args\n+        filelist = []\n+        foreach(source, sources_list) {\n+          filelist += [ rebase_path(\"$source\", root_build_dir) ]\n+        }\n+        foreach(base_path, output_file_base_paths) {\n+          outputs += [\n+            \"$root_gen_dir/${base_path}${variant_dash_suffix}-forward.h\",\n+            \"$root_gen_dir/${base_path}${variant_dash_suffix}-import-headers.h\",\n+            \"$root_gen_dir/${base_path}${variant_dash_suffix}-test-utils.cc\",\n+            \"$root_gen_dir/${base_path}${variant_dash_suffix}-test-utils.h\",\n+            \"$root_gen_dir/${base_path}${variant_dash_suffix}.cc\",\n+            \"$root_gen_dir/${base_path}${variant_dash_suffix}.h\",\n+          ]\n+          if (generate_fuzzing && !defined(bindings_configuration.variant)) {\n+            outputs += [\n+              \"$root_gen_dir/${base_path}${variant_dash_suffix}-mojolpm.cc\",\n+              \"$root_gen_dir/${base_path}${variant_dash_suffix}-mojolpm.h\",\n+            ]\n+          }\n+        }\n+\n+        response_file_contents = filelist\n+\n+        args += [\n+          \"--filelist={{response_file_name}}\",\n+          \"-g\",\n+        ]\n+\n+        if (generate_fuzzing && !defined(bindings_configuration.variant)) {\n+          args += [ \"c++,mojolpm\" ]\n+        } else {\n+          args += [ \"c++\" ]\n+        }\n+\n+        if (defined(bindings_configuration.variant)) {\n+          args += [\n+            \"--variant\",\n+            bindings_configuration.variant,\n+          ]\n+        }\n+\n+        args += [\n+          \"--typemap\",\n+          rebase_path(type_mappings_path, root_build_dir),\n+        ]\n+\n+        if (defined(bindings_configuration.for_blink) &&\n+            bindings_configuration.for_blink) {\n+          args += [ \"--for_blink\" ]\n+        }\n+\n+        if (defined(invoker.support_lazy_serialization) &&\n+            invoker.support_lazy_serialization) {\n+          args += [ \"--support_lazy_serialization\" ]\n+        }\n+\n+        if (enable_kythe_annotations) {\n+          args += [ \"--enable_kythe_annotations\" ]\n+        }\n+\n+        if (!defined(invoker.scramble_message_ids) ||\n+            invoker.scramble_message_ids) {\n+          inputs += message_scrambling_inputs\n+          args += message_scrambling_args\n+        }\n+\n+        if (defined(invoker.extra_cpp_template_paths)) {\n+          foreach(extra_cpp_template, invoker.extra_cpp_template_paths) {\n+            args += [\n+              \"--extra_cpp_template_paths\",\n+              rebase_path(extra_cpp_template, root_build_dir),\n+            ]\n+            assert(\n+                get_path_info(extra_cpp_template, \"extension\") == \"tmpl\",\n+                \"--extra_cpp_template_paths only accepts template files ending in extension .tmpl\")\n+            foreach(base_path, output_file_base_paths) {\n+              template_file_name = get_path_info(\"$extra_cpp_template\", \"name\")\n+              outputs += [ \"$root_gen_dir/${base_path}${variant_dash_suffix}-${template_file_name}\" ]\n+            }\n+          }\n+        }\n+      }\n+    }\n+\n+    if (generate_fuzzing && !defined(variant)) {\n+      # This block contains the C++ targets for the MojoLPM fuzzer, we need to\n+      # do this here so that we can use the typemap configuration for the\n+      # empty-variant Mojo target.\n+\n+      mojolpm_target_name = \"${target_name}_mojolpm\"\n+      mojolpm_generator_target_name = \"${target_name}__generator\"\n+      source_set(mojolpm_target_name) {\n+        # There are still a few missing header dependencies between mojo targets\n+        # with typemaps and the dependencies of their typemap headers. It would\n+        # be good to enable include checking for these in the future though.\n+        check_includes = false\n+        testonly = true\n+        if (defined(invoker.sources)) {\n+          sources = process_file_template(\n+                  invoker.sources,\n+                  [\n+                    \"{{source_gen_dir}}/{{source_file_part}}-mojolpm.cc\",\n+                    \"{{source_gen_dir}}/{{source_file_part}}-mojolpm.h\",\n+                  ])\n+          deps = []\n+        } else {\n+          sources = []\n+          deps = []\n+        }\n+\n+        public_deps = [\n+          \":$generator_shared_target_name\",\n+\n+          # NB: hardcoded dependency on the no-variant variant generator, since\n+          # mojolpm only uses the no-variant type.\n+          \":$mojolpm_generator_target_name\",\n+          \":$mojolpm_proto_target_name\",\n+          \"//mojo/public/tools/fuzzers:mojolpm\",\n+        ]\n+\n+        foreach(d, all_deps) {\n+          # Resolve the name, so that a target //mojo/something becomes\n+          # //mojo/something:something and we can append variant_suffix to\n+          # get the cpp dependency name.\n+          full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+          public_deps += [ \"${full_name}_mojolpm\" ]\n+        }\n+\n+        foreach(typemap, active_typemaps) {\n+          _typemap_config = {\n+          }\n+          _typemap_config = typemap.config\n+\n+          if (defined(_typemap_config.deps)) {\n+            deps += _typemap_config.deps\n+          }\n+          if (defined(_typemap_config.public_deps)) {\n+            public_deps += _typemap_config.public_deps\n+          }\n+        }\n+        foreach(config, cpp_typemap_configs) {\n+          if (defined(config.traits_deps)) {\n+            deps += config.traits_deps\n+          }\n+          if (defined(config.traits_public_deps)) {\n+            public_deps += config.traits_public_deps\n+          }\n+        }\n+      }\n+    }\n+\n+    # Write the typemapping configuration for this target out to a file to be\n+    # validated by a Python script. This helps catch mistakes that can't\n+    # be caught by logic in GN.\n+    _typemap_config_filename =\n+        \"$target_gen_dir/${target_name}${variant_suffix}.typemap_config\"\n+    _typemap_stamp_filename = \"${_typemap_config_filename}.validated\"\n+    _typemap_validator_target_name = \"${type_mappings_target_name}__validator\"\n+    _rebased_typemap_configs = []\n+    foreach(config, cpp_typemap_configs) {\n+      _rebased_config = {\n+      }\n+      _rebased_config = config\n+      if (defined(config.traits_headers)) {\n+        _rebased_config.traits_headers = []\n+        _rebased_config.traits_headers =\n+            rebase_path(config.traits_headers, \"//\")\n+      }\n+      if (defined(config.traits_private_headers)) {\n+        _rebased_config.traits_private_headers = []\n+        _rebased_config.traits_private_headers =\n+            rebase_path(config.traits_private_headers, \"//\")\n+      }\n+      _rebased_typemap_configs += [ _rebased_config ]\n+    }\n+    write_file(_typemap_config_filename, _rebased_typemap_configs, \"json\")\n+    _mojom_target_name = target_name\n+    action(_typemap_validator_target_name) {\n+      script = \"$mojom_generator_root/validate_typemap_config.py\"\n+      inputs = [ _typemap_config_filename ]\n+      outputs = [ _typemap_stamp_filename ]\n+      args = [\n+        get_label_info(_mojom_target_name, \"label_no_toolchain\"),\n+        rebase_path(_typemap_config_filename),\n+        rebase_path(_typemap_stamp_filename),\n+      ]\n+    }\n+\n+    action(type_mappings_target_name) {\n+      inputs = _bindings_configuration_files + mojom_generator_sources +\n+               jinja2_sources + [ _typemap_stamp_filename ]\n+      outputs = [ type_mappings_path ]\n+      script = \"$mojom_generator_root/generate_type_mappings.py\"\n+      deps = [ \":$_typemap_validator_target_name\" ]\n+      args = [\n+        \"--output\",\n+        rebase_path(type_mappings_path, root_build_dir),\n+      ]\n+\n+      foreach(d, all_deps) {\n+        name = get_label_info(d, \"label_no_toolchain\")\n+        toolchain = get_label_info(d, \"toolchain\")\n+        dependency_output = \"${name}${variant_suffix}__type_mappings\"\n+        dependency_target = \"${dependency_output}(${toolchain})\"\n+        deps += [ dependency_target ]\n+        dependency_output_dir =\n+            get_label_info(dependency_output, \"target_gen_dir\")\n+        dependency_name = get_label_info(dependency_output, \"name\")\n+        dependency_path =\n+            rebase_path(\"$dependency_output_dir/${dependency_name}\",\n+                        root_build_dir)\n+        args += [\n+          \"--dependency\",\n+          dependency_path,\n+        ]\n+      }\n+\n+      if (sources_list != []) {\n+        # TODO(sammc): Pass the typemap description in a file to avoid command\n+        # line length limitations.\n+        typemap_description = []\n+        foreach(typemap, active_typemaps) {\n+          _typemap_config = {\n+          }\n+          _typemap_config = typemap.config\n+\n+          typemap_description += [ \"--start-typemap\" ]\n+          if (defined(_typemap_config.public_headers)) {\n+            foreach(value, _typemap_config.public_headers) {\n+              typemap_description += [ \"public_headers=$value\" ]\n+            }\n+          }\n+          if (defined(_typemap_config.traits_headers)) {\n+            foreach(value, _typemap_config.traits_headers) {\n+              typemap_description += [ \"traits_headers=$value\" ]\n+            }\n+          }\n+          foreach(value, _typemap_config.type_mappings) {\n+            typemap_description += [ \"type_mappings=$value\" ]\n+          }\n+\n+          # The typemap configuration files are not actually used as inputs here\n+          # but this establishes a necessary build dependency to ensure that\n+          # typemap changes force a rebuild of affected targets.\n+          if (defined(typemap.filename)) {\n+            inputs += [ typemap.filename ]\n+          }\n+        }\n+        args += typemap_description\n+\n+        # Newer GN-based typemaps are aggregated into a single config.\n+        inputs += [ _typemap_config_filename ]\n+        args += [\n+          \"--cpp-typemap-config\",\n+          rebase_path(_typemap_config_filename, root_build_dir),\n+        ]\n+      }\n+    }\n+\n+    group(\"${target_name}${variant_suffix}_headers\") {\n+      public_deps = []\n+      if (sources_list != []) {\n+        public_deps += [\n+          \":$generator_cpp_message_ids_target_name\",\n+          \":$generator_shared_target_name\",\n+          \":$generator_target_name\",\n+        ]\n+      }\n+      foreach(d, all_deps) {\n+        full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+        public_deps += [ \"${full_name}${variant_suffix}_headers\" ]\n+      }\n+    }\n+\n+    if (!force_source_set && defined(invoker.component_macro_prefix)) {\n+      output_target_type = \"component\"\n+    } else {\n+      output_target_type = \"source_set\"\n+    }\n+\n+    js_data_deps_target_name = target_name + \"_js_data_deps\"\n+    not_needed([ \"js_data_deps_target_name\" ])\n+\n+    target(\"jumbo_\" + output_target_type, \"${target_name}${variant_suffix}\") {\n+      if (defined(output_name_override)) {\n+        output_name = output_name_override\n+      }\n+      if (defined(bindings_configuration.for_blink) &&\n+          bindings_configuration.for_blink &&\n+          defined(invoker.visibility_blink)) {\n+        visibility = invoker.visibility_blink\n+      } else if (defined(invoker.visibility)) {\n+        visibility = invoker.visibility\n+      }\n+      if (defined(invoker.testonly)) {\n+        testonly = invoker.testonly\n+      }\n+      defines = export_defines\n+      if (output_file_base_paths != []) {\n+        sources = []\n+        foreach(base_path, output_file_base_paths) {\n+          foreach(suffix, generator_cpp_output_suffixes) {\n+            sources += [ \"$root_gen_dir/${base_path}$suffix\" ]\n+          }\n+        }\n+      }\n+      deps = [\n+        \":$generator_cpp_message_ids_target_name\",\n+        \"//mojo/public/cpp/bindings:struct_traits\",\n+        \"//mojo/public/interfaces/bindings:bindings_headers\",\n+      ]\n+      public_deps = [\n+        \":$shared_cpp_library_target_name\",\n+        \"//base\",\n+      ]\n+      if (require_full_cpp_deps) {\n+        public_deps += [ \"//mojo/public/cpp/bindings\" ]\n+      } else {\n+        public_deps += [ \"//mojo/public/cpp/bindings:bindings_base\" ]\n+      }\n+\n+      if (sources_list != []) {\n+        public_deps += [ \":$generator_target_name\" ]\n+      }\n+      foreach(d, all_deps) {\n+        # Resolve the name, so that a target //mojo/something becomes\n+        # //mojo/something:something and we can append variant_suffix to\n+        # get the cpp dependency name.\n+        full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+        public_deps += [ \"${full_name}${variant_suffix}\" ]\n+      }\n+      if (defined(bindings_configuration.for_blink) &&\n+          bindings_configuration.for_blink) {\n+        if (defined(invoker.overridden_deps_blink)) {\n+          foreach(d, invoker.overridden_deps_blink) {\n+            # Resolve the name, so that a target //mojo/something becomes\n+            # //mojo/something:something and we can append variant_suffix\n+            # to get the cpp dependency name.\n+            full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+            public_deps -= [ \"${full_name}${variant_suffix}\" ]\n+          }\n+          public_deps += invoker.component_deps_blink\n+        }\n+        if (defined(invoker.check_includes_blink)) {\n+          check_includes = invoker.check_includes_blink\n+        }\n+      } else {\n+        if (defined(invoker.check_includes_blink)) {\n+          not_needed(invoker, [ \"check_includes_blink\" ])\n+        }\n+        if (defined(invoker.overridden_deps)) {\n+          foreach(d, invoker.overridden_deps) {\n+            # Resolve the name, so that a target //mojo/something becomes\n+            # //mojo/something:something and we can append variant_suffix\n+            # to get the cpp dependency name.\n+            full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+            public_deps -= [ \"${full_name}${variant_suffix}\" ]\n+          }\n+          public_deps += invoker.component_deps\n+        }\n+      }\n+      foreach(typemap, active_typemaps) {\n+        _typemap_config = {\n+        }\n+        _typemap_config = typemap.config\n+        if (defined(_typemap_config.sources)) {\n+          sources += _typemap_config.sources\n+        }\n+        if (defined(_typemap_config.public_deps)) {\n+          public_deps += _typemap_config.public_deps\n+        }\n+        if (defined(_typemap_config.deps)) {\n+          deps += _typemap_config.deps\n+        }\n+      }\n+      foreach(config, cpp_typemap_configs) {\n+        if (defined(config.traits_sources)) {\n+          sources += config.traits_sources\n+        }\n+        if (defined(config.traits_deps)) {\n+          deps += config.traits_deps\n+        }\n+        if (defined(config.traits_public_deps)) {\n+          public_deps += config.traits_public_deps\n+        }\n+      }\n+      if (defined(invoker.export_header)) {\n+        sources += [ \"//\" + invoker.export_header ]\n+      }\n+      if (defined(bindings_configuration.for_blink) &&\n+          bindings_configuration.for_blink) {\n+        public_deps += [ \"//mojo/public/cpp/bindings:wtf_support\" ]\n+      }\n+\n+      if (generate_fuzzing) {\n+        # Generate JS bindings by default if IPC fuzzer is enabled.\n+        public_deps += [ \":$js_data_deps_target_name\" ]\n+      }\n+    }\n+\n+    if (generate_java && is_android) {\n+      import(\"//build/config/android/rules.gni\")\n+\n+      java_generator_target_name = target_name + \"_java__generator\"\n+      if (sources_list != []) {\n+        action(java_generator_target_name) {\n+          script = mojom_generator_script\n+          inputs = mojom_generator_sources + jinja2_sources\n+          sources = sources_list\n+          deps = [\n+            \":$parser_target_name\",\n+            \":$type_mappings_target_name\",\n+            \"//mojo/public/tools/bindings:precompile_templates\",\n+          ]\n+          outputs = []\n+          args = common_generator_args\n+          filelist = []\n+          foreach(source, sources_list) {\n+            filelist += [ rebase_path(\"$source\", root_build_dir) ]\n+          }\n+          foreach(base_path, output_file_base_paths) {\n+            outputs += [ \"$root_gen_dir/$base_path.srcjar\" ]\n+          }\n+\n+          response_file_contents = filelist\n+\n+          args += [\n+            \"--filelist={{response_file_name}}\",\n+            \"-g\",\n+            \"java\",\n+          ]\n+\n+          if (!defined(invoker.scramble_message_ids) ||\n+              invoker.scramble_message_ids) {\n+            inputs += message_scrambling_inputs\n+            args += message_scrambling_args\n+          }\n+        }\n+      } else {\n+        group(java_generator_target_name) {\n+        }\n+      }\n+\n+      java_srcjar_target_name = target_name + \"_java_sources\"\n+      action(java_srcjar_target_name) {\n+        script = \"//build/android/gyp/zip.py\"\n+        inputs = []\n+        if (output_file_base_paths != []) {\n+          foreach(base_path, output_file_base_paths) {\n+            inputs += [ \"$root_gen_dir/${base_path}.srcjar\" ]\n+          }\n+        }\n+        output = \"$target_gen_dir/$target_name.srcjar\"\n+        outputs = [ output ]\n+        rebase_inputs = rebase_path(inputs, root_build_dir)\n+        rebase_output = rebase_path(output, root_build_dir)\n+        args = [\n+          \"--input-zips=$rebase_inputs\",\n+          \"--output=$rebase_output\",\n+        ]\n+        deps = []\n+        if (sources_list != []) {\n+          deps = [ \":$java_generator_target_name\" ]\n+        }\n+      }\n+\n+      java_target_name = target_name + \"_java\"\n+      android_library(java_target_name) {\n+        forward_variables_from(invoker, [ \"enable_bytecode_checks\" ])\n+        deps = [\n+          \"//base:base_java\",\n+          \"//mojo/public/java:bindings_java\",\n+          \"//mojo/public/java:system_java\",\n+        ]\n+\n+        # Disable warnings/checks on these generated files.\n+        chromium_code = false\n+\n+        foreach(d, all_deps) {\n+          # Resolve the name, so that a target //mojo/something becomes\n+          # //mojo/something:something and we can append \"_java\" to get the java\n+          # dependency name.\n+          full_name = get_label_info(d, \"label_no_toolchain\")\n+          deps += [ \"${full_name}_java\" ]\n+        }\n+\n+        srcjar_deps = [ \":$java_srcjar_target_name\" ]\n+      }\n+    }\n+  }\n+\n+  use_typescript_for_target =\n+      enable_typescript_bindings && defined(invoker.use_typescript_sources) &&\n+      invoker.use_typescript_sources\n+\n+  if (!use_typescript_for_target && defined(invoker.use_typescript_sources)) {\n+    not_needed(invoker, [ \"use_typescript_sources\" ])\n+  }\n+\n+  if ((generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) &&\n+      !use_typescript_for_target) {\n+    if (sources_list != []) {\n+      generator_js_target_name = \"${target_name}_js__generator\"\n+      action(generator_js_target_name) {\n+        script = mojom_generator_script\n+        inputs = mojom_generator_sources + jinja2_sources\n+        sources = sources_list\n+        deps = [\n+          \":$parser_target_name\",\n+          \"//mojo/public/tools/bindings:precompile_templates\",\n+        ]\n+        if (defined(invoker.parser_deps)) {\n+          deps += invoker.parser_deps\n+        }\n+        outputs = []\n+        args = common_generator_args\n+        filelist = []\n+        foreach(source, sources_list) {\n+          filelist += [ rebase_path(\"$source\", root_build_dir) ]\n+        }\n+        foreach(base_path, output_file_base_paths) {\n+          outputs += [\n+            \"$root_gen_dir/$base_path.js\",\n+            \"$root_gen_dir/$base_path.externs.js\",\n+            \"$root_gen_dir/$base_path-lite.js\",\n+            \"$root_gen_dir/$base_path.html\",\n+            \"$root_gen_dir/$base_path-lite-for-compile.js\",\n+          ]\n+        }\n+\n+        response_file_contents = filelist\n+\n+        args += [\n+          \"--filelist={{response_file_name}}\",\n+          \"-g\",\n+          \"javascript\",\n+          \"--js_bindings_mode=new\",\n+        ]\n+\n+        if (defined(invoker.js_generate_struct_deserializers) &&\n+            invoker.js_generate_struct_deserializers) {\n+          args += [ \"--js_generate_struct_deserializers\" ]\n+        }\n+\n+        if (!defined(invoker.scramble_message_ids) ||\n+            invoker.scramble_message_ids) {\n+          inputs += message_scrambling_inputs\n+          args += message_scrambling_args\n+        }\n+\n+        if (generate_fuzzing) {\n+          args += [ \"--generate_fuzzing\" ]\n+        }\n+      }\n+    }\n+\n+    js_target_name = target_name + \"_js\"\n+    group(js_target_name) {\n+      public_deps = []\n+      if (sources_list != []) {\n+        public_deps += [ \":$generator_js_target_name\" ]\n+      }\n+\n+      foreach(d, all_deps) {\n+        full_name = get_label_info(d, \"label_no_toolchain\")\n+        public_deps += [ \"${full_name}_js\" ]\n+      }\n+    }\n+\n+    group(js_data_deps_target_name) {\n+      deps = []\n+      if (sources_list != []) {\n+        data = []\n+        foreach(base_path, output_file_base_paths) {\n+          data += [\n+            \"$root_gen_dir/${base_path}.js\",\n+            \"$root_gen_dir/${base_path}-lite.js\",\n+          ]\n+        }\n+        deps += [ \":$generator_js_target_name\" ]\n+      }\n+\n+      data_deps = []\n+      foreach(d, all_deps) {\n+        full_name = get_label_info(d, \"label_no_toolchain\")\n+        data_deps += [ \"${full_name}_js_data_deps\" ]\n+      }\n+    }\n+\n+    js_library_target_name = \"${target_name}_js_library\"\n+    if (sources_list != []) {\n+      js_library(js_library_target_name) {\n+        extra_public_deps = [ \":$generator_js_target_name\" ]\n+        sources = []\n+        foreach(base_path, output_file_base_paths) {\n+          sources += [ \"$root_gen_dir/${base_path}-lite.js\" ]\n+        }\n+        externs_list = [\n+          \"${externs_path}/mojo_core.js\",\n+          \"${externs_path}/pending.js\",\n+        ]\n+\n+        deps = []\n+        foreach(d, all_deps) {\n+          full_name = get_label_info(d, \"label_no_toolchain\")\n+          deps += [ \"${full_name}_js_library\" ]\n+        }\n+      }\n+    } else {\n+      group(js_library_target_name) {\n+      }\n+    }\n+\n+    js_library_for_compile_target_name = \"${target_name}_js_library_for_compile\"\n+    if (sources_list != []) {\n+      js_library(js_library_for_compile_target_name) {\n+        extra_public_deps = [ \":$generator_js_target_name\" ]\n+        sources = []\n+        foreach(base_path, output_file_base_paths) {\n+          sources += [ \"$root_gen_dir/${base_path}-lite-for-compile.js\" ]\n+        }\n+        externs_list = [\n+          \"${externs_path}/mojo_core.js\",\n+          \"${externs_path}/pending.js\",\n+        ]\n+        deps = []\n+        if (!defined(invoker.disallow_native_types)) {\n+          deps += [ \"//mojo/public/js:bindings_lite_sources\" ]\n+        }\n+        foreach(d, all_deps) {\n+          full_name = get_label_info(d, \"label_no_toolchain\")\n+          deps += [ \"${full_name}_js_library_for_compile\" ]\n+        }\n+      }\n+    } else {\n+      group(js_library_for_compile_target_name) {\n+      }\n+    }\n+  }\n+  if ((generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) &&\n+      use_typescript_for_target) {\n+    generator_js_target_names = []\n+    source_filelist = []\n+    foreach(source, sources_list) {\n+      source_filelist += [ rebase_path(\"$source\", root_build_dir) ]\n+    }\n+\n+    dependency_types = [\n+      {\n+        name = \"regular\"\n+        ts_extension = \".ts\"\n+        js_extension = \".js\"\n+      },\n+      {\n+        name = \"es_modules\"\n+        ts_extension = \".m.ts\"\n+        js_extension = \".m.js\"\n+      },\n+    ]\n+\n+    foreach(dependency_type, dependency_types) {\n+      ts_outputs = []\n+      js_outputs = []\n+\n+      foreach(base_path, output_file_base_paths) {\n+        ts_outputs +=\n+            [ \"$root_gen_dir/$base_path-lite${dependency_type.ts_extension}\" ]\n+        js_outputs +=\n+            [ \"$root_gen_dir/$base_path-lite${dependency_type.js_extension}\" ]\n+      }\n+\n+      # Generate Typescript bindings.\n+      generator_ts_target_name =\n+          \"${target_name}_${dependency_type.name}__ts__generator\"\n+      action(generator_ts_target_name) {\n+        script = mojom_generator_script\n+        inputs = mojom_generator_sources + jinja2_sources\n+        sources = sources_list\n+        deps = [\n+          \":$parser_target_name\",\n+          \"//mojo/public/tools/bindings:precompile_templates\",\n+        ]\n+\n+        outputs = ts_outputs\n+        args = common_generator_args\n+        response_file_contents = source_filelist\n+\n+        args += [\n+          \"--filelist={{response_file_name}}\",\n+          \"-g\",\n+          \"typescript\",\n+        ]\n+\n+        if (dependency_type.name == \"es_modules\") {\n+          args += [ \"--ts_use_es_modules\" ]\n+        }\n+\n+        # TODO(crbug.com/1007587): Support scramble_message_ids.\n+        # TODO(crbug.com/1007591): Support generate_fuzzing.\n+      }\n+\n+      # Create tsconfig.json for the generated Typescript.\n+      tsconfig_filename =\n+          \"$target_gen_dir/$target_name-${dependency_type.name}-tsconfig.json\"\n+      tsconfig = {\n+      }\n+      tsconfig.compilerOptions = {\n+        composite = true\n+        target = \"es6\"\n+        module = \"es6\"\n+        lib = [\n+          \"es6\",\n+          \"esnext.bigint\",\n+        ]\n+        strict = true\n+      }\n+      tsconfig.files = []\n+      foreach(base_path, output_file_base_paths) {\n+        tsconfig.files += [ rebase_path(\n+                \"$root_gen_dir/$base_path-lite${dependency_type.ts_extension}\",\n+                target_gen_dir,\n+                root_gen_dir) ]\n+      }\n+      tsconfig.references = []\n+\n+      # Get tsconfigs for deps.\n+      foreach(d, all_deps) {\n+        dep_target_gen_dir = rebase_path(get_label_info(d, \"target_gen_dir\"))\n+        dep_name = get_label_info(d, \"name\")\n+        reference = {\n+        }\n+        reference.path = \"$dep_target_gen_dir/$dep_name-${dependency_type.name}-tsconfig.json\"\n+        tsconfig.references += [ reference ]\n+      }\n+      write_file(tsconfig_filename, tsconfig, \"json\")\n+\n+      # Compile previously generated Typescript to Javascript.\n+      generator_js_target_name =\n+          \"${target_name}_${dependency_type.name}__js__generator\"\n+      generator_js_target_names += [ generator_js_target_name ]\n+\n+      action(generator_js_target_name) {\n+        script = \"$mojom_generator_root/compile_typescript.py\"\n+        sources = ts_outputs\n+        outputs = js_outputs\n+        public_deps = [ \":$generator_ts_target_name\" ]\n+        foreach(d, all_deps) {\n+          full_name = get_label_info(d, \"label_no_toolchain\")\n+          public_deps +=\n+              [ \"${full_name}_${dependency_type.name}__js__generator\" ]\n+        }\n+\n+        absolute_tsconfig_path =\n+            rebase_path(tsconfig_filename, \"\", target_gen_dir)\n+        args = [ \"--tsconfig_path=$absolute_tsconfig_path\" ]\n+      }\n+    }\n+\n+    js_target_name = target_name + \"_js\"\n+    group(js_target_name) {\n+      public_deps = []\n+      if (sources_list != []) {\n+        foreach(generator_js_target_name, generator_js_target_names) {\n+          public_deps += [ \":$generator_js_target_name\" ]\n+        }\n+      }\n+\n+      foreach(d, all_deps) {\n+        full_name = get_label_info(d, \"label_no_toolchain\")\n+        public_deps += [ \"${full_name}_js\" ]\n+      }\n+    }\n+\n+    group(js_data_deps_target_name) {\n+      data = js_outputs\n+      deps = []\n+      foreach(generator_js_target_name, generator_js_target_names) {\n+        deps += [ \":$generator_js_target_name\" ]\n+      }\n+      data_deps = []\n+      foreach(d, all_deps) {\n+        full_name = get_label_info(d, \"label_no_toolchain\")\n+        data_deps += [ \"${full_name}_js_data_deps\" ]\n+      }\n+    }\n+  }\n+}\n+\n+# A helper for the mojom() template above when component libraries are desired\n+# for generated C++ bindings units. Supports all the same arguments as mojom()\n+# except for the optional |component_output_prefix| and |component_macro_prefix|\n+# arguments. These are instead shortened to |output_prefix| and |macro_prefix|\n+# and are *required*.\n+template(\"mojom_component\") {\n+  assert(defined(invoker.output_prefix) && defined(invoker.macro_prefix))\n+\n+  mojom(target_name) {\n+    forward_variables_from(invoker,\n+                           \"*\",\n+                           [\n+                             \"output_prefix\",\n+                             \"macro_prefix\",\n+                           ])\n+    component_output_prefix = invoker.output_prefix\n+    component_macro_prefix = invoker.macro_prefix\n+  }\n+}\ndiff --git a/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator.py b/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator.py\nnew file mode 100755\nindex 00000000..da9efc71\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator.py\n@@ -0,0 +1,390 @@\n+#!/usr/bin/env python\n+# Copyright 2013 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+\"\"\"The frontend for the Mojo bindings system.\"\"\"\n+\n+from __future__ import print_function\n+\n+import argparse\n+\n+import hashlib\n+import importlib\n+import json\n+import os\n+import pprint\n+import re\n+import struct\n+import sys\n+\n+# Disable lint check for finding modules:\n+# pylint: disable=F0401\n+\n+def _GetDirAbove(dirname):\n+  \"\"\"Returns the directory \"above\" this file containing |dirname| (which must\n+  also be \"above\" this file).\"\"\"\n+  path = os.path.abspath(__file__)\n+  while True:\n+    path, tail = os.path.split(path)\n+    assert tail\n+    if tail == dirname:\n+      return path\n+\n+\n+sys.path.insert(\n+    0,\n+    os.path.join(\n+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"mojom\"))\n+\n+from mojom.error import Error\n+import mojom.fileutil as fileutil\n+from mojom.generate.module import Module\n+from mojom.generate import template_expander\n+from mojom.generate import translate\n+from mojom.generate.generator import WriteFile\n+\n+sys.path.append(\n+    os.path.join(_GetDirAbove(\"mojo\"), \"tools\", \"diagnosis\"))\n+import crbug_1001171\n+\n+\n+_BUILTIN_GENERATORS = {\n+    \"c++\": \"mojom_cpp_generator\",\n+    \"javascript\": \"mojom_js_generator\",\n+    \"java\": \"mojom_java_generator\",\n+    \"mojolpm\": \"mojom_mojolpm_generator\",\n+    \"typescript\": \"mojom_ts_generator\",\n+}\n+\n+\n+def LoadGenerators(generators_string):\n+  if not generators_string:\n+    return []  # No generators.\n+\n+  generators = {}\n+  for generator_name in [s.strip() for s in generators_string.split(\",\")]:\n+    language = generator_name.lower()\n+    if language not in _BUILTIN_GENERATORS:\n+      print(\"Unknown generator name %s\" % generator_name)\n+      sys.exit(1)\n+    generator_module = importlib.import_module(\n+        \"generators.%s\" % _BUILTIN_GENERATORS[language])\n+    generators[language] = generator_module\n+  return generators\n+\n+\n+def MakeImportStackMessage(imported_filename_stack):\n+  \"\"\"Make a (human-readable) message listing a chain of imports. (Returned\n+  string begins with a newline (if nonempty) and does not end with one.)\"\"\"\n+  return ''.join(\n+      reversed([\"\\n  %s was imported by %s\" % (a, b) for (a, b) in \\\n+                    zip(imported_filename_stack[1:], imported_filename_stack)]))\n+\n+\n+class RelativePath(object):\n+  \"\"\"Represents a path relative to the source tree or generated output dir.\"\"\"\n+\n+  def __init__(self, path, source_root, output_dir):\n+    self.path = path\n+    if path.startswith(source_root):\n+      self.root = source_root\n+    elif path.startswith(output_dir):\n+      self.root = output_dir\n+    else:\n+      raise Exception(\"Invalid input path %s\" % path)\n+\n+  def relative_path(self):\n+    return os.path.relpath(\n+        os.path.abspath(self.path), os.path.abspath(self.root))\n+\n+\n+def _GetModulePath(path, output_dir):\n+  return os.path.join(output_dir, path.relative_path() + '-module')\n+\n+\n+def ScrambleMethodOrdinals(interfaces, salt):\n+  already_generated = set()\n+  for interface in interfaces:\n+    i = 0\n+    already_generated.clear()\n+    for method in interface.methods:\n+      if method.explicit_ordinal is not None:\n+        continue\n+      while True:\n+        i = i + 1\n+        if i == 1000000:\n+          raise Exception(\"Could not generate %d method ordinals for %s\" %\n+              (len(interface.methods), interface.mojom_name))\n+        # Generate a scrambled method.ordinal value. The algorithm doesn't have\n+        # to be very strong, cryptographically. It just needs to be non-trivial\n+        # to guess the results without the secret salt, in order to make it\n+        # harder for a compromised process to send fake Mojo messages.\n+        sha256 = hashlib.sha256(salt)\n+        sha256.update(interface.mojom_name.encode('utf-8'))\n+        sha256.update(str(i).encode('utf-8'))\n+        # Take the first 4 bytes as a little-endian uint32.\n+        ordinal = struct.unpack('<L', sha256.digest()[:4])[0]\n+        # Trim to 31 bits, so it always fits into a Java (signed) int.\n+        ordinal = ordinal & 0x7fffffff\n+        if ordinal in already_generated:\n+          continue\n+        already_generated.add(ordinal)\n+        method.ordinal = ordinal\n+        method.ordinal_comment = (\n+            'The %s value is based on sha256(salt + \"%s%d\").' %\n+            (ordinal, interface.mojom_name, i))\n+        break\n+\n+\n+def ReadFileContents(filename):\n+  with open(filename, 'rb') as f:\n+    return f.read()\n+\n+\n+class MojomProcessor(object):\n+  \"\"\"Takes parsed mojom modules and generates language bindings from them.\n+\n+  Attributes:\n+    _processed_files: {Dict[str, mojom.generate.module.Module]} Mapping from\n+        relative mojom filename paths to the module AST for that mojom file.\n+  \"\"\"\n+  def __init__(self, should_generate):\n+    self._should_generate = should_generate\n+    self._processed_files = {}\n+    self._typemap = {}\n+\n+  def LoadTypemaps(self, typemaps):\n+    # Support some very simple single-line comments in typemap JSON.\n+    comment_expr = r\"^\\s*//.*$\"\n+    def no_comments(line):\n+      return not re.match(comment_expr, line)\n+    for filename in typemaps:\n+      with open(filename) as f:\n+        typemaps = json.loads(\"\".join(filter(no_comments, f.readlines())))\n+        for language, typemap in typemaps.items():\n+          language_map = self._typemap.get(language, {})\n+          language_map.update(typemap)\n+          self._typemap[language] = language_map\n+    if 'c++' in self._typemap:\n+      self._typemap['mojolpm'] = self._typemap['c++']\n+\n+  def _GenerateModule(self, args, remaining_args, generator_modules,\n+                      rel_filename, imported_filename_stack):\n+    # Return the already-generated module.\n+    if rel_filename.path in self._processed_files:\n+      return self._processed_files[rel_filename.path]\n+\n+    if rel_filename.path in imported_filename_stack:\n+      print(\"%s: Error: Circular dependency\" % rel_filename.path + \\\n+          MakeImportStackMessage(imported_filename_stack + [rel_filename.path]))\n+      sys.exit(1)\n+\n+    module_path = _GetModulePath(rel_filename, args.output_dir)\n+    with open(module_path, 'rb') as f:\n+      module = Module.Load(f)\n+\n+    if args.scrambled_message_id_salt_paths:\n+      salt = b''.join(\n+          map(ReadFileContents, args.scrambled_message_id_salt_paths))\n+      ScrambleMethodOrdinals(module.interfaces, salt)\n+\n+    if self._should_generate(rel_filename.path):\n+      for language, generator_module in generator_modules.items():\n+        generator = generator_module.Generator(\n+            module, args.output_dir, typemap=self._typemap.get(language, {}),\n+            variant=args.variant, bytecode_path=args.bytecode_path,\n+            for_blink=args.for_blink,\n+            js_bindings_mode=args.js_bindings_mode,\n+            js_generate_struct_deserializers=\\\n+                    args.js_generate_struct_deserializers,\n+            export_attribute=args.export_attribute,\n+            export_header=args.export_header,\n+            generate_non_variant_code=args.generate_non_variant_code,\n+            support_lazy_serialization=args.support_lazy_serialization,\n+            disallow_native_types=args.disallow_native_types,\n+            disallow_interfaces=args.disallow_interfaces,\n+            generate_message_ids=args.generate_message_ids,\n+            generate_fuzzing=args.generate_fuzzing,\n+            enable_kythe_annotations=args.enable_kythe_annotations,\n+            extra_cpp_template_paths=args.extra_cpp_template_paths,\n+            generate_extra_cpp_only=args.generate_extra_cpp_only)\n+        filtered_args = []\n+        if hasattr(generator_module, 'GENERATOR_PREFIX'):\n+          prefix = '--' + generator_module.GENERATOR_PREFIX + '_'\n+          filtered_args = [arg for arg in remaining_args\n+                           if arg.startswith(prefix)]\n+        generator.GenerateFiles(filtered_args)\n+\n+    # Save result.\n+    self._processed_files[rel_filename.path] = module\n+    return module\n+\n+\n+def _Generate(args, remaining_args):\n+  if args.variant == \"none\":\n+    args.variant = None\n+\n+  for idx, import_dir in enumerate(args.import_directories):\n+    tokens = import_dir.split(\":\")\n+    if len(tokens) >= 2:\n+      args.import_directories[idx] = RelativePath(tokens[0], tokens[1],\n+                                                  args.output_dir)\n+    else:\n+      args.import_directories[idx] = RelativePath(tokens[0], args.depth,\n+                                                  args.output_dir)\n+  generator_modules = LoadGenerators(args.generators_string)\n+\n+  fileutil.EnsureDirectoryExists(args.output_dir)\n+\n+  processor = MojomProcessor(lambda filename: filename in args.filename)\n+  processor.LoadTypemaps(set(args.typemaps))\n+\n+  if args.filelist:\n+    with open(args.filelist) as f:\n+      args.filename.extend(f.read().split())\n+\n+  for filename in args.filename:\n+    processor._GenerateModule(\n+        args, remaining_args, generator_modules,\n+        RelativePath(filename, args.depth, args.output_dir), [])\n+\n+  return 0\n+\n+\n+def _Precompile(args, _):\n+  generator_modules = LoadGenerators(\",\".join(_BUILTIN_GENERATORS.keys()))\n+\n+  template_expander.PrecompileTemplates(generator_modules, args.output_dir)\n+  return 0\n+\n+\n+def main():\n+  parser = argparse.ArgumentParser(\n+      description=\"Generate bindings from mojom files.\")\n+  parser.add_argument(\"--use_bundled_pylibs\", action=\"store_true\",\n+                      help=\"use Python modules bundled in the SDK\")\n+  parser.add_argument(\n+      \"-o\",\n+      \"--output_dir\",\n+      dest=\"output_dir\",\n+      default=\".\",\n+      help=\"output directory for generated files\")\n+\n+  subparsers = parser.add_subparsers()\n+\n+  generate_parser = subparsers.add_parser(\n+      \"generate\", description=\"Generate bindings from mojom files.\")\n+  generate_parser.add_argument(\"filename\", nargs=\"*\",\n+                               help=\"mojom input file\")\n+  generate_parser.add_argument(\"--filelist\", help=\"mojom input file list\")\n+  generate_parser.add_argument(\"-d\", \"--depth\", dest=\"depth\", default=\".\",\n+                               help=\"depth from source root\")\n+  generate_parser.add_argument(\"-g\",\n+                               \"--generators\",\n+                               dest=\"generators_string\",\n+                               metavar=\"GENERATORS\",\n+                               default=\"c++,javascript,java,mojolpm\",\n+                               help=\"comma-separated list of generators\")\n+  generate_parser.add_argument(\n+      \"--gen_dir\", dest=\"gen_directories\", action=\"append\", metavar=\"directory\",\n+      default=[], help=\"add a directory to be searched for the syntax trees.\")\n+  generate_parser.add_argument(\n+      \"-I\", dest=\"import_directories\", action=\"append\", metavar=\"directory\",\n+      default=[],\n+      help=\"add a directory to be searched for import files. The depth from \"\n+           \"source root can be specified for each import by appending it after \"\n+           \"a colon\")\n+  generate_parser.add_argument(\"--typemap\", action=\"append\", metavar=\"TYPEMAP\",\n+                               default=[], dest=\"typemaps\",\n+                               help=\"apply TYPEMAP to generated output\")\n+  generate_parser.add_argument(\"--variant\", dest=\"variant\", default=None,\n+                               help=\"output a named variant of the bindings\")\n+  generate_parser.add_argument(\n+      \"--bytecode_path\", required=True, help=(\n+          \"the path from which to load template bytecode; to generate template \"\n+          \"bytecode, run %s precompile BYTECODE_PATH\" % os.path.basename(\n+              sys.argv[0])))\n+  generate_parser.add_argument(\"--for_blink\", action=\"store_true\",\n+                               help=\"Use WTF types as generated types for mojo \"\n+                               \"string/array/map.\")\n+  generate_parser.add_argument(\n+      \"--js_bindings_mode\", choices=[\"new\", \"old\"], default=\"old\",\n+      help=\"This option only affects the JavaScript bindings. The value could \"\n+      \"be \\\"new\\\" to generate new-style lite JS bindings in addition to the \"\n+      \"old, or \\\"old\\\" to only generate old bindings.\")\n+  generate_parser.add_argument(\n+      \"--js_generate_struct_deserializers\", action=\"store_true\",\n+      help=\"Generate javascript deserialize methods for structs in \"\n+      \"mojom-lite.js file\")\n+  generate_parser.add_argument(\n+      \"--export_attribute\", default=\"\",\n+      help=\"Optional attribute to specify on class declaration to export it \"\n+      \"for the component build.\")\n+  generate_parser.add_argument(\n+      \"--export_header\", default=\"\",\n+      help=\"Optional header to include in the generated headers to support the \"\n+      \"component build.\")\n+  generate_parser.add_argument(\n+      \"--generate_non_variant_code\", action=\"store_true\",\n+      help=\"Generate code that is shared by different variants.\")\n+  generate_parser.add_argument(\n+      \"--scrambled_message_id_salt_path\",\n+      dest=\"scrambled_message_id_salt_paths\",\n+      help=\"If non-empty, the path to a file whose contents should be used as\"\n+      \"a salt for generating scrambled message IDs. If this switch is specified\"\n+      \"more than once, the contents of all salt files are concatenated to form\"\n+      \"the salt value.\", default=[], action=\"append\")\n+  generate_parser.add_argument(\n+      \"--support_lazy_serialization\",\n+      help=\"If set, generated bindings will serialize lazily when possible.\",\n+      action=\"store_true\")\n+  generate_parser.add_argument(\n+      \"--extra_cpp_template_paths\",\n+      dest=\"extra_cpp_template_paths\",\n+      action=\"append\",\n+      metavar=\"path_to_template\",\n+      default=[],\n+      help=\"Provide a path to a new template (.tmpl) that is used to generate \"\n+      \"additional C++ source/header files \")\n+  generate_parser.add_argument(\n+      \"--generate_extra_cpp_only\",\n+      help=\"If set and extra_cpp_template_paths provided, will only generate\"\n+      \"extra_cpp_template related C++ bindings\",\n+      action=\"store_true\")\n+  generate_parser.add_argument(\n+      \"--disallow_native_types\",\n+      help=\"Disallows the [Native] attribute to be specified on structs or \"\n+      \"enums within the mojom file.\", action=\"store_true\")\n+  generate_parser.add_argument(\n+      \"--disallow_interfaces\",\n+      help=\"Disallows interface definitions within the mojom file. It is an \"\n+      \"error to specify this flag when processing a mojom file which defines \"\n+      \"any interface.\", action=\"store_true\")\n+  generate_parser.add_argument(\n+      \"--generate_message_ids\",\n+      help=\"Generates only the message IDs header for C++ bindings. Note that \"\n+      \"this flag only matters if --generate_non_variant_code is also \"\n+      \"specified.\", action=\"store_true\")\n+  generate_parser.add_argument(\n+      \"--generate_fuzzing\",\n+      action=\"store_true\",\n+      help=\"Generates additional bindings for fuzzing in JS.\")\n+  generate_parser.add_argument(\n+      \"--enable_kythe_annotations\",\n+      action=\"store_true\",\n+      help=\"Adds annotations for kythe metadata generation.\")\n+\n+  generate_parser.set_defaults(func=_Generate)\n+\n+  precompile_parser = subparsers.add_parser(\"precompile\",\n+      description=\"Precompile templates for the mojom bindings generator.\")\n+  precompile_parser.set_defaults(func=_Precompile)\n+\n+  args, remaining_args = parser.parse_known_args()\n+  return args.func(args, remaining_args)\n+\n+\n+if __name__ == \"__main__\":\n+  with crbug_1001171.DumpStateOnLookupError():\n+    sys.exit(main())\ndiff --git a/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py b/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py\nnew file mode 100644\nindex 00000000..bddbe3f4\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py\n@@ -0,0 +1,62 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import unittest\n+\n+from mojom_bindings_generator import MakeImportStackMessage\n+from mojom_bindings_generator import ScrambleMethodOrdinals\n+\n+\n+class FakeIface(object):\n+  def __init__(self):\n+    self.mojom_name = None\n+    self.methods = None\n+\n+\n+class FakeMethod(object):\n+  def __init__(self, explicit_ordinal=None):\n+    self.explicit_ordinal = explicit_ordinal\n+    self.ordinal = explicit_ordinal\n+    self.ordinal_comment = None\n+\n+\n+class MojoBindingsGeneratorTest(unittest.TestCase):\n+  \"\"\"Tests mojo_bindings_generator.\"\"\"\n+\n+  def testMakeImportStackMessage(self):\n+    \"\"\"Tests MakeImportStackMessage().\"\"\"\n+    self.assertEqual(MakeImportStackMessage([\"x\"]), \"\")\n+    self.assertEqual(MakeImportStackMessage([\"x\", \"y\"]),\n+                     \"\\n  y was imported by x\")\n+    self.assertEqual(MakeImportStackMessage([\"x\", \"y\", \"z\"]),\n+                     \"\\n  z was imported by y\\n  y was imported by x\")\n+\n+  def testScrambleMethodOrdinals(self):\n+    \"\"\"Tests ScrambleMethodOrdinals().\"\"\"\n+    interface = FakeIface()\n+    interface.mojom_name = 'RendererConfiguration'\n+    interface.methods = [\n+        FakeMethod(),\n+        FakeMethod(),\n+        FakeMethod(),\n+        FakeMethod(explicit_ordinal=42)\n+    ]\n+    ScrambleMethodOrdinals([interface], \"foo\".encode('utf-8'))\n+    # These next three values are hard-coded. If the generation algorithm\n+    # changes from being based on sha256(seed + interface.name + str(i)) then\n+    # these numbers will obviously need to change too.\n+    #\n+    # Note that hashlib.sha256('fooRendererConfiguration1').digest()[:4] is\n+    # '\\xa5\\xbc\\xf9\\xca' and that hex(1257880741) = '0x4af9bca5'. The\n+    # difference in 0x4a vs 0xca is because we only take 31 bits.\n+    self.assertEqual(interface.methods[0].ordinal, 1257880741)\n+    self.assertEqual(interface.methods[1].ordinal, 631133653)\n+    self.assertEqual(interface.methods[2].ordinal, 549336076)\n+\n+    # Explicit method ordinals should not be scrambled.\n+    self.assertEqual(interface.methods[3].ordinal, 42)\n+\n+\n+if __name__ == \"__main__\":\n+  unittest.main()\ndiff --git a/utils/ipc/mojo/public/tools/bindings/mojom_types_downgrader.py b/utils/ipc/mojo/public/tools/bindings/mojom_types_downgrader.py\nnew file mode 100755\nindex 00000000..15f0e3ba\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/mojom_types_downgrader.py\n@@ -0,0 +1,119 @@\n+#!/usr/bin/env python\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Downgrades *.mojom files to the old mojo types for remotes and receivers.\"\"\"\n+\n+import argparse\n+import fnmatch\n+import os\n+import re\n+import shutil\n+import sys\n+import tempfile\n+\n+# List of patterns and replacements to match and use against the contents of a\n+# mojo file. Each replacement string will be used with Python string's format()\n+# function, so the '{}' substring is used to mark where the mojo type should go.\n+_MOJO_REPLACEMENTS = {\n+    r'pending_remote': r'{}',\n+    r'pending_receiver': r'{}&',\n+    r'pending_associated_remote': r'associated {}',\n+    r'pending_associated_receiver': r'associated {}&',\n+}\n+\n+# Pre-compiled regular expression that matches against any of the replacements.\n+_REGEXP_PATTERN = re.compile(\n+    r'|'.join(\n+        ['{}\\s*<\\s*(.*?)\\s*>'.format(k) for k in _MOJO_REPLACEMENTS.keys()]),\n+    flags=re.DOTALL)\n+\n+\n+def ReplaceFunction(match_object):\n+  \"\"\"Returns the right replacement for the string matched against the regexp.\"\"\"\n+  for index, (match, repl) in enumerate(_MOJO_REPLACEMENTS.items(), 1):\n+    if match_object.group(0).startswith(match):\n+      return repl.format(match_object.group(index))\n+\n+\n+def DowngradeFile(path, output_dir=None):\n+  \"\"\"Downgrades the mojom file specified by |path| to the old mojo types.\n+\n+  Optionally pass |output_dir| to place the result under a separate output\n+  directory, preserving the relative path to the file included in |path|.\n+  \"\"\"\n+  # Use a temporary file to dump the new contents after replacing the patterns.\n+  with open(path) as src_mojo_file:\n+    with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp_mojo_file:\n+      tmp_contents = _REGEXP_PATTERN.sub(ReplaceFunction, src_mojo_file.read())\n+      tmp_mojo_file.write(tmp_contents)\n+\n+  # Files should be placed in the desired output directory\n+  if output_dir:\n+    output_filepath = os.path.join(output_dir, os.path.basename(path))\n+    if not os.path.exists(output_dir):\n+      os.makedirs(output_dir)\n+  else:\n+    output_filepath = path\n+\n+  # Write the new contents preserving the original file's attributes.\n+  shutil.copystat(path, tmp_mojo_file.name)\n+  shutil.move(tmp_mojo_file.name, output_filepath)\n+\n+  # Make sure to \"touch\" the new file so that access, modify and change times\n+  # are always newer than the source file's, otherwise Modify time will be kept\n+  # as per the call to shutil.copystat(), causing unnecessary generations of the\n+  # output file in subsequent builds due to ninja considering it dirty.\n+  os.utime(output_filepath, None)\n+\n+\n+def DowngradeDirectory(path, output_dir=None):\n+  \"\"\"Downgrades mojom files inside directory |path| to the old mojo types.\n+\n+  Optionally pass |output_dir| to place the result under a separate output\n+  directory, preserving the relative path to the file included in |path|.\n+  \"\"\"\n+  # We don't have recursive glob.glob() nor pathlib.Path.rglob() in Python 2.7\n+  mojom_filepaths = []\n+  for dir_path, _, filenames in os.walk(path):\n+    for filename in fnmatch.filter(filenames, \"*mojom\"):\n+      mojom_filepaths.append(os.path.join(dir_path, filename))\n+\n+  for path in mojom_filepaths:\n+    absolute_dirpath = os.path.dirname(os.path.abspath(path))\n+    if output_dir:\n+      dest_dirpath = output_dir + absolute_dirpath\n+    else:\n+      dest_dirpath = absolute_dirpath\n+    DowngradeFile(path, dest_dirpath)\n+\n+\n+def DowngradePath(src_path, output_dir=None):\n+  \"\"\"Downgrades the mojom files pointed by |src_path| to the old mojo types.\n+\n+  Optionally pass |output_dir| to place the result under a separate output\n+  directory, preserving the relative path to the file included in |path|.\n+  \"\"\"\n+  if os.path.isdir(src_path):\n+    DowngradeDirectory(src_path, output_dir)\n+  elif os.path.isfile(src_path):\n+    DowngradeFile(src_path, output_dir)\n+  else:\n+    print(\">>> {} not pointing to a valid file or directory\".format(src_path))\n+    sys.exit(1)\n+\n+\n+def main():\n+  parser = argparse.ArgumentParser(\n+      description=\"Downgrade *.mojom files to use the old mojo types.\")\n+  parser.add_argument(\n+      \"srcpath\", help=\"path to the file or directory to apply the conversion\")\n+  parser.add_argument(\n+      \"--outdir\", help=\"the directory to place the converted file(s) under\")\n+  args = parser.parse_args()\n+\n+  DowngradePath(args.srcpath, args.outdir)\n+\n+\n+if __name__ == \"__main__\":\n+  sys.exit(main())\ndiff --git a/utils/ipc/mojo/public/tools/bindings/validate_typemap_config.py b/utils/ipc/mojo/public/tools/bindings/validate_typemap_config.py\nnew file mode 100755\nindex 00000000..f1783d59\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/bindings/validate_typemap_config.py\n@@ -0,0 +1,57 @@\n+#!/usr/bin/env python\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import argparse\n+import json\n+import os\n+import re\n+import sys\n+\n+\n+def CheckCppTypemapConfigs(target_name, config_filename, out_filename):\n+  _SUPPORTED_CONFIG_KEYS = set([\n+      'types', 'traits_headers', 'traits_private_headers', 'traits_sources',\n+      'traits_deps', 'traits_public_deps'\n+  ])\n+  _SUPPORTED_TYPE_KEYS = set([\n+      'mojom', 'cpp', 'copyable_pass_by_value', 'force_serialize', 'hashable',\n+      'move_only', 'nullable_is_same_type'\n+  ])\n+  with open(config_filename, 'r') as f:\n+    for config in json.load(f):\n+      for key in config.keys():\n+        if key not in _SUPPORTED_CONFIG_KEYS:\n+          raise ValueError('Invalid typemap property \"%s\" when processing %s' %\n+                           (key, target_name))\n+\n+      types = config.get('types')\n+      if not types:\n+        raise ValueError('Typemap for %s must specify at least one type to map'\n+                         % target_name)\n+\n+      for entry in types:\n+        for key in entry.keys():\n+          if key not in _SUPPORTED_TYPE_KEYS:\n+            raise IOError(\n+                'Invalid type property \"%s\" in typemap for \"%s\" on target %s' %\n+                (key, entry.get('mojom', '(unknown)'), target_name))\n+\n+  with open(out_filename, 'w') as f:\n+    f.truncate(0)\n+\n+\n+def main():\n+  parser = argparse.ArgumentParser()\n+  _, args = parser.parse_known_args()\n+  if len(args) != 3:\n+    print('Usage: validate_typemap_config.py target_name config_filename '\n+          'stamp_filename')\n+    sys.exit(1)\n+\n+  CheckCppTypemapConfigs(args[0], args[1], args[2])\n+\n+\n+if __name__ == '__main__':\n+  main()\ndiff --git a/utils/ipc/mojo/public/tools/mojom/README.md b/utils/ipc/mojo/public/tools/mojom/README.md\nnew file mode 100644\nindex 00000000..6a4ff78a\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/README.md\n@@ -0,0 +1,14 @@\n+# The Mojom Parser\n+\n+The Mojom format is an interface definition language (IDL) for describing\n+interprocess communication (IPC) messages and data types for use with the\n+low-level cross-platform\n+[Mojo IPC library](https://chromium.googlesource.com/chromium/src/+/master/mojo/public/c/system/README.md).\n+\n+This directory consists of a `mojom` Python module, its tests, and supporting\n+command-line tools. The Python module implements the parser used by the\n+command-line tools and exposes an API to help external bindings generators emit\n+useful code from the parser's outputs.\n+\n+TODO(https://crbug.com/1060464): Fill out this documentation once the library\n+and tools have stabilized.\ndiff --git a/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py\nnew file mode 100755\nindex 00000000..7e746112\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py\n@@ -0,0 +1,170 @@\n+#!/usr/bin/env python\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Verifies backward-compatibility of mojom type changes.\n+\n+Given a set of pre- and post-diff mojom file contents, and a root directory\n+for a project, this tool verifies that any changes to [Stable] mojom types are\n+backward-compatible with the previous version.\n+\n+This can be used e.g. by a presubmit check to prevent developers from making\n+breaking changes to stable mojoms.\"\"\"\n+\n+import argparse\n+import errno\n+import io\n+import json\n+import os\n+import os.path\n+import shutil\n+import six\n+import sys\n+import tempfile\n+\n+from mojom.generate import module\n+from mojom.generate import translate\n+from mojom.parse import parser\n+\n+\n+class ParseError(Exception):\n+  pass\n+\n+\n+def _ValidateDelta(root, delta):\n+  \"\"\"Parses all modified mojoms (including all transitive mojom dependencies,\n+  even if unmodified) to perform backward-compatibility checks on any types\n+  marked with the [Stable] attribute.\n+\n+  Note that unlike the normal build-time parser in mojom_parser.py, this does\n+  not produce or rely on cached module translations, but instead parses the full\n+  transitive closure of a mojom's input dependencies all at once.\n+  \"\"\"\n+\n+  # First build a map of all files covered by the delta\n+  affected_files = set()\n+  old_files = {}\n+  new_files = {}\n+  for change in delta:\n+    # TODO(crbug.com/953884): Use pathlib once we're migrated fully to Python 3.\n+    filename = change['filename'].replace('\\\\', '/')\n+    affected_files.add(filename)\n+    if change['old']:\n+      old_files[filename] = change['old']\n+    if change['new']:\n+      new_files[filename] = change['new']\n+\n+  # Parse and translate all mojoms relevant to the delta, including transitive\n+  # imports that weren't modified.\n+  unmodified_modules = {}\n+\n+  def parseMojom(mojom, file_overrides, override_modules):\n+    if mojom in unmodified_modules or mojom in override_modules:\n+      return\n+\n+    contents = file_overrides.get(mojom)\n+    if contents:\n+      modules = override_modules\n+    else:\n+      modules = unmodified_modules\n+      with io.open(os.path.join(root, mojom), encoding='utf-8') as f:\n+        contents = f.read()\n+\n+    try:\n+      ast = parser.Parse(contents, mojom)\n+    except Exception as e:\n+      six.reraise(\n+          ParseError,\n+          'encountered exception {0} while parsing {1}'.format(e, mojom),\n+          sys.exc_info()[2])\n+    for imp in ast.import_list:\n+      parseMojom(imp.import_filename, file_overrides, override_modules)\n+\n+    # Now that the transitive set of dependencies has been imported and parsed\n+    # above, translate each mojom AST into a Module so that all types are fully\n+    # defined and can be inspected.\n+    all_modules = {}\n+    all_modules.update(unmodified_modules)\n+    all_modules.update(override_modules)\n+    modules[mojom] = translate.OrderedModule(ast, mojom, all_modules)\n+\n+  old_modules = {}\n+  for mojom in old_files.keys():\n+    parseMojom(mojom, old_files, old_modules)\n+  new_modules = {}\n+  for mojom in new_files.keys():\n+    parseMojom(mojom, new_files, new_modules)\n+\n+  # At this point we have a complete set of translated Modules from both the\n+  # pre- and post-diff mojom contents. Now we can analyze backward-compatibility\n+  # of the deltas.\n+  #\n+  # Note that for backward-compatibility checks we only care about types which\n+  # were marked [Stable] before the diff. Types newly marked as [Stable] are not\n+  # checked.\n+  def collectTypes(modules):\n+    types = {}\n+    for m in modules.values():\n+      for kinds in (m.enums, m.structs, m.unions, m.interfaces):\n+        for kind in kinds:\n+          types[kind.qualified_name] = kind\n+    return types\n+\n+  old_types = collectTypes(old_modules)\n+  new_types = collectTypes(new_modules)\n+\n+  # Collect any renamed types so they can be compared accordingly.\n+  renamed_types = {}\n+  for name, kind in new_types.items():\n+    old_name = kind.attributes and kind.attributes.get('RenamedFrom')\n+    if old_name:\n+      renamed_types[old_name] = name\n+\n+  for qualified_name, kind in old_types.items():\n+    if not kind.stable:\n+      continue\n+\n+    new_name = renamed_types.get(qualified_name, qualified_name)\n+    if new_name not in new_types:\n+      raise Exception(\n+          'Stable type %s appears to be deleted by this change. If it was '\n+          'renamed, please add a [RenamedFrom] attribute to the new type. This '\n+          'can be deleted by a subsequent change.' % qualified_name)\n+\n+    if not new_types[new_name].IsBackwardCompatible(kind):\n+      raise Exception('Stable type %s appears to have changed in a way which '\n+                      'breaks backward-compatibility. Please fix!\\n\\nIf you '\n+                      'believe this assessment to be incorrect, please file a '\n+                      'Chromium bug against the \"Internals>Mojo>Bindings\" '\n+                      'component.' % qualified_name)\n+\n+\n+def Run(command_line, delta=None):\n+  \"\"\"Runs the tool with the given command_line. Normally this will read the\n+  change description from stdin as a JSON-encoded list, but tests may pass a\n+  delta directly for convenience.\"\"\"\n+  arg_parser = argparse.ArgumentParser(\n+      description='Verifies backward-compatibility of mojom type changes.',\n+      epilog=\"\"\"\n+This tool reads a change description from stdin and verifies that all modified\n+[Stable] mojom types will retain backward-compatibility. The change description\n+must be a JSON-encoded list of objects, each with a \"filename\" key (path to a\n+changed mojom file, relative to ROOT); an \"old\" key whose value is a string of\n+the full file contents before the change, or null if the file is being added;\n+and a \"new\" key whose value is a string of the full file contents after the\n+change, or null if the file is being deleted.\"\"\")\n+  arg_parser.add_argument(\n+      '--src-root',\n+      required=True,\n+      action='store',\n+      metavar='ROOT',\n+      help='The root of the source tree in which the checked mojoms live.')\n+\n+  args, _ = arg_parser.parse_known_args(command_line)\n+  if not delta:\n+    delta = json.load(sys.stdin)\n+  _ValidateDelta(args.src_root, delta)\n+\n+\n+if __name__ == '__main__':\n+  Run(sys.argv[1:])\ndiff --git a/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility_unittest.py b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility_unittest.py\nnew file mode 100755\nindex 00000000..9f51ea77\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility_unittest.py\n@@ -0,0 +1,260 @@\n+#!/usr/bin/env python\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import json\n+import os\n+import os.path\n+import shutil\n+import tempfile\n+import unittest\n+\n+import check_stable_mojom_compatibility\n+\n+from mojom.generate import module\n+\n+\n+class Change(object):\n+  \"\"\"Helper to clearly define a mojom file delta to be analyzed.\"\"\"\n+\n+  def __init__(self, filename, old=None, new=None):\n+    \"\"\"If old is None, this is a file addition. If new is None, this is a file\n+    deletion. Otherwise it's a file change.\"\"\"\n+    self.filename = filename\n+    self.old = old\n+    self.new = new\n+\n+\n+class UnchangedFile(Change):\n+  def __init__(self, filename, contents):\n+    super(UnchangedFile, self).__init__(filename, old=contents, new=contents)\n+\n+\n+class CheckStableMojomCompatibilityTest(unittest.TestCase):\n+  \"\"\"Tests covering the behavior of the compatibility checking tool. Note that\n+  details of different compatibility checks and relevant failure modes are NOT\n+  covered by these tests. Those are instead covered by unittests in\n+  version_compatibility_unittest.py. Additionally, the tests which ensure a\n+  given set of [Stable] mojom definitions are indeed plausibly stable (i.e. they\n+  have no unstable dependencies) are covered by stable_attribute_unittest.py.\n+\n+  These tests cover higher-level concerns of the compatibility checking tool,\n+  like file or symbol, renames, changes spread over multiple files, etc.\"\"\"\n+\n+  def verifyBackwardCompatibility(self, changes):\n+    \"\"\"Helper for implementing assertBackwardCompatible and\n+    assertNotBackwardCompatible\"\"\"\n+\n+    temp_dir = tempfile.mkdtemp()\n+    for change in changes:\n+      if change.old:\n+        # Populate the old file on disk in our temporary fake source root\n+        file_path = os.path.join(temp_dir, change.filename)\n+        dir_path = os.path.dirname(file_path)\n+        if not os.path.exists(dir_path):\n+          os.makedirs(dir_path)\n+        with open(file_path, 'w') as f:\n+          f.write(change.old)\n+\n+    delta = []\n+    for change in changes:\n+      if change.old != change.new:\n+        delta.append({\n+            'filename': change.filename,\n+            'old': change.old,\n+            'new': change.new\n+        })\n+\n+    try:\n+      check_stable_mojom_compatibility.Run(['--src-root', temp_dir],\n+                                           delta=delta)\n+    finally:\n+      shutil.rmtree(temp_dir)\n+\n+  def assertBackwardCompatible(self, changes):\n+    self.verifyBackwardCompatibility(changes)\n+\n+  def assertNotBackwardCompatible(self, changes):\n+    try:\n+      self.verifyBackwardCompatibility(changes)\n+    except Exception:\n+      return\n+\n+    raise Exception('Change unexpectedly passed a backward-compatibility check')\n+\n+  def testBasicCompatibility(self):\n+    \"\"\"Minimal smoke test to verify acceptance of a simple valid change.\"\"\"\n+    self.assertBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old='[Stable] struct S {};',\n+               new='[Stable] struct S { [MinVersion=1] int32 x; };')\n+    ])\n+\n+  def testBasicIncompatibility(self):\n+    \"\"\"Minimal smoke test to verify rejection of a simple invalid change.\"\"\"\n+    self.assertNotBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old='[Stable] struct S {};',\n+               new='[Stable] struct S { int32 x; };')\n+    ])\n+\n+  def testIgnoreIfNotStable(self):\n+    \"\"\"We don't care about types not marked [Stable]\"\"\"\n+    self.assertBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old='struct S {};',\n+               new='struct S { int32 x; };')\n+    ])\n+\n+  def testRename(self):\n+    \"\"\"We can do checks for renamed types.\"\"\"\n+    self.assertBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old='[Stable] struct S {};',\n+               new='[Stable, RenamedFrom=\"S\"] struct T {};')\n+    ])\n+    self.assertNotBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old='[Stable] struct S {};',\n+               new='[Stable, RenamedFrom=\"S\"] struct T { int32 x; };')\n+    ])\n+    self.assertBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old='[Stable] struct S {};',\n+               new=\"\"\"\\\n+               [Stable, RenamedFrom=\"S\"]\n+               struct T { [MinVersion=1] int32 x; };\n+               \"\"\")\n+    ])\n+\n+  def testNewlyStable(self):\n+    \"\"\"We don't care about types newly marked as [Stable].\"\"\"\n+    self.assertBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old='struct S {};',\n+               new='[Stable] struct S { int32 x; };')\n+    ])\n+\n+  def testFileRename(self):\n+    \"\"\"Make sure we can still do compatibility checks after a file rename.\"\"\"\n+    self.assertBackwardCompatible([\n+        Change('foo/foo.mojom', old='[Stable] struct S {};', new=None),\n+        Change('bar/bar.mojom',\n+               old=None,\n+               new='[Stable] struct S { [MinVersion=1] int32 x; };')\n+    ])\n+    self.assertNotBackwardCompatible([\n+        Change('foo/foo.mojom', old='[Stable] struct S {};', new=None),\n+        Change('bar/bar.mojom', old=None, new='[Stable] struct S { int32 x; };')\n+    ])\n+\n+  def testWithImport(self):\n+    \"\"\"Ensure that cross-module dependencies do not break the compatibility\n+    checking tool.\"\"\"\n+    self.assertBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old=\"\"\"\\\n+               module foo;\n+               [Stable] struct S {};\n+               \"\"\",\n+               new=\"\"\"\\\n+               module foo;\n+               [Stable] struct S { [MinVersion=2] int32 x; };\n+               \"\"\"),\n+        Change('bar/bar.mojom',\n+               old=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable] struct T { foo.S s; };\n+               \"\"\",\n+               new=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable] struct T { foo.S s; [MinVersion=1] int32 y; };\n+               \"\"\")\n+    ])\n+\n+  def testWithMovedDefinition(self):\n+    \"\"\"If a definition moves from one file to another, we should still be able\n+    to check compatibility accurately.\"\"\"\n+    self.assertBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old=\"\"\"\\\n+               module foo;\n+               [Stable] struct S {};\n+               \"\"\",\n+               new=\"\"\"\\\n+               module foo;\n+               \"\"\"),\n+        Change('bar/bar.mojom',\n+               old=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable] struct T { foo.S s; };\n+               \"\"\",\n+               new=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable, RenamedFrom=\"foo.S\"] struct S {\n+                 [MinVersion=2] int32 x;\n+               };\n+               [Stable] struct T { S s; [MinVersion=1] int32 y; };\n+               \"\"\")\n+    ])\n+\n+    self.assertNotBackwardCompatible([\n+        Change('foo/foo.mojom',\n+               old=\"\"\"\\\n+               module foo;\n+               [Stable] struct S {};\n+               \"\"\",\n+               new=\"\"\"\\\n+               module foo;\n+               \"\"\"),\n+        Change('bar/bar.mojom',\n+               old=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable] struct T { foo.S s; };\n+               \"\"\",\n+               new=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable, RenamedFrom=\"foo.S\"] struct S { int32 x; };\n+               [Stable] struct T { S s; [MinVersion=1] int32 y; };\n+               \"\"\")\n+    ])\n+\n+  def testWithUnmodifiedImport(self):\n+    \"\"\"Unchanged files in the filesystem are still parsed by the compatibility\n+    checking tool if they're imported by a changed file.\"\"\"\n+    self.assertBackwardCompatible([\n+        UnchangedFile('foo/foo.mojom', 'module foo; [Stable] struct S {};'),\n+        Change('bar/bar.mojom',\n+               old=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable] struct T { foo.S s; };\n+               \"\"\",\n+               new=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable] struct T { foo.S s; [MinVersion=1] int32 x; };\n+               \"\"\")\n+    ])\n+\n+    self.assertNotBackwardCompatible([\n+        UnchangedFile('foo/foo.mojom', 'module foo; [Stable] struct S {};'),\n+        Change('bar/bar.mojom',\n+               old=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable] struct T { foo.S s; };\n+               \"\"\",\n+               new=\"\"\"\\\n+               module bar;\n+               import \"foo/foo.mojom\";\n+               [Stable] struct T { foo.S s; int32 x; };\n+               \"\"\")\n+    ])\ndiff --git a/utils/ipc/mojo/public/tools/mojom/const_unittest.py b/utils/ipc/mojo/public/tools/mojom/const_unittest.py\nnew file mode 100644\nindex 00000000..cb42dfac\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/const_unittest.py\n@@ -0,0 +1,90 @@\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+from mojom_parser_test_case import MojomParserTestCase\n+from mojom.generate import module as mojom\n+\n+\n+class ConstTest(MojomParserTestCase):\n+  \"\"\"Tests constant parsing behavior.\"\"\"\n+\n+  def testLiteralInt(self):\n+    a_mojom = 'a.mojom'\n+    self.WriteFile(a_mojom, 'const int32 k = 42;')\n+    self.ParseMojoms([a_mojom])\n+    a = self.LoadModule(a_mojom)\n+    self.assertEqual(1, len(a.constants))\n+    self.assertEqual('k', a.constants[0].mojom_name)\n+    self.assertEqual('42', a.constants[0].value)\n+\n+  def testLiteralFloat(self):\n+    a_mojom = 'a.mojom'\n+    self.WriteFile(a_mojom, 'const float k = 42.5;')\n+    self.ParseMojoms([a_mojom])\n+    a = self.LoadModule(a_mojom)\n+    self.assertEqual(1, len(a.constants))\n+    self.assertEqual('k', a.constants[0].mojom_name)\n+    self.assertEqual('42.5', a.constants[0].value)\n+\n+  def testLiteralString(self):\n+    a_mojom = 'a.mojom'\n+    self.WriteFile(a_mojom, 'const string k = \"woot\";')\n+    self.ParseMojoms([a_mojom])\n+    a = self.LoadModule(a_mojom)\n+    self.assertEqual(1, len(a.constants))\n+    self.assertEqual('k', a.constants[0].mojom_name)\n+    self.assertEqual('\"woot\"', a.constants[0].value)\n+\n+  def testEnumConstant(self):\n+    a_mojom = 'a.mojom'\n+    self.WriteFile(a_mojom, 'module a; enum E { kA = 41, kB };')\n+    b_mojom = 'b.mojom'\n+    self.WriteFile(\n+        b_mojom, \"\"\"\\\n+      import \"a.mojom\";\n+      const a.E kE1 = a.E.kB;\n+\n+      // We also allow value names to be unqualified, implying scope from the\n+      // constant's type.\n+      const a.E kE2 = kB;\n+      \"\"\")\n+    self.ParseMojoms([a_mojom, b_mojom])\n+    a = self.LoadModule(a_mojom)\n+    b = self.LoadModule(b_mojom)\n+    self.assertEqual(1, len(a.enums))\n+    self.assertEqual('E', a.enums[0].mojom_name)\n+    self.assertEqual(2, len(b.constants))\n+    self.assertEqual('kE1', b.constants[0].mojom_name)\n+    self.assertEqual(a.enums[0], b.constants[0].kind)\n+    self.assertEqual(a.enums[0].fields[1], b.constants[0].value.field)\n+    self.assertEqual(42, b.constants[0].value.field.numeric_value)\n+    self.assertEqual('kE2', b.constants[1].mojom_name)\n+    self.assertEqual(a.enums[0].fields[1], b.constants[1].value.field)\n+    self.assertEqual(42, b.constants[1].value.field.numeric_value)\n+\n+  def testConstantReference(self):\n+    a_mojom = 'a.mojom'\n+    self.WriteFile(a_mojom, 'const int32 kA = 42; const int32 kB = kA;')\n+    self.ParseMojoms([a_mojom])\n+    a = self.LoadModule(a_mojom)\n+    self.assertEqual(2, len(a.constants))\n+    self.assertEqual('kA', a.constants[0].mojom_name)\n+    self.assertEqual('42', a.constants[0].value)\n+    self.assertEqual('kB', a.constants[1].mojom_name)\n+    self.assertEqual('42', a.constants[1].value)\n+\n+  def testImportedConstantReference(self):\n+    a_mojom = 'a.mojom'\n+    self.WriteFile(a_mojom, 'const int32 kA = 42;')\n+    b_mojom = 'b.mojom'\n+    self.WriteFile(b_mojom, 'import \"a.mojom\"; const int32 kB = kA;')\n+    self.ParseMojoms([a_mojom, b_mojom])\n+    a = self.LoadModule(a_mojom)\n+    b = self.LoadModule(b_mojom)\n+    self.assertEqual(1, len(a.constants))\n+    self.assertEqual(1, len(b.constants))\n+    self.assertEqual('kA', a.constants[0].mojom_name)\n+    self.assertEqual('42', a.constants[0].value)\n+    self.assertEqual('kB', b.constants[0].mojom_name)\n+    self.assertEqual('42', b.constants[0].value)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/enum_unittest.py b/utils/ipc/mojo/public/tools/mojom/enum_unittest.py\nnew file mode 100644\nindex 00000000..d9005078\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/enum_unittest.py\n@@ -0,0 +1,92 @@\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+from mojom_parser_test_case import MojomParserTestCase\n+\n+\n+class EnumTest(MojomParserTestCase):\n+  \"\"\"Tests enum parsing behavior.\"\"\"\n+\n+  def testExplicitValues(self):\n+    \"\"\"Verifies basic parsing of assigned integral values.\"\"\"\n+    types = self.ExtractTypes('enum E { kFoo=0, kBar=2, kBaz };')\n+    self.assertEqual('kFoo', types['E'].fields[0].mojom_name)\n+    self.assertEqual(0, types['E'].fields[0].numeric_value)\n+    self.assertEqual('kBar', types['E'].fields[1].mojom_name)\n+    self.assertEqual(2, types['E'].fields[1].numeric_value)\n+    self.assertEqual('kBaz', types['E'].fields[2].mojom_name)\n+    self.assertEqual(3, types['E'].fields[2].numeric_value)\n+\n+  def testImplicitValues(self):\n+    \"\"\"Verifies basic automatic assignment of integral values at parse time.\"\"\"\n+    types = self.ExtractTypes('enum E { kFoo, kBar, kBaz };')\n+    self.assertEqual('kFoo', types['E'].fields[0].mojom_name)\n+    self.assertEqual(0, types['E'].fields[0].numeric_value)\n+    self.assertEqual('kBar', types['E'].fields[1].mojom_name)\n+    self.assertEqual(1, types['E'].fields[1].numeric_value)\n+    self.assertEqual('kBaz', types['E'].fields[2].mojom_name)\n+    self.assertEqual(2, types['E'].fields[2].numeric_value)\n+\n+  def testSameEnumReference(self):\n+    \"\"\"Verifies that an enum value can be assigned from the value of another\n+    field within the same enum.\"\"\"\n+    types = self.ExtractTypes('enum E { kA, kB, kFirst=kA };')\n+    self.assertEqual('kA', types['E'].fields[0].mojom_name)\n+    self.assertEqual(0, types['E'].fields[0].numeric_value)\n+    self.assertEqual('kB', types['E'].fields[1].mojom_name)\n+    self.assertEqual(1, types['E'].fields[1].numeric_value)\n+    self.assertEqual('kFirst', types['E'].fields[2].mojom_name)\n+    self.assertEqual(0, types['E'].fields[2].numeric_value)\n+\n+  def testSameModuleOtherEnumReference(self):\n+    \"\"\"Verifies that an enum value can be assigned from the value of a field\n+    in another enum within the same module.\"\"\"\n+    types = self.ExtractTypes('enum E { kA, kB }; enum F { kA = E.kB };')\n+    self.assertEqual(1, types['F'].fields[0].numeric_value)\n+\n+  def testImportedEnumReference(self):\n+    \"\"\"Verifies that an enum value can be assigned from the value of a field\n+    in another enum within a different module.\"\"\"\n+    a_mojom = 'a.mojom'\n+    self.WriteFile(a_mojom, 'module a; enum E { kFoo=42, kBar };')\n+    b_mojom = 'b.mojom'\n+    self.WriteFile(b_mojom,\n+                   'module b; import \"a.mojom\"; enum F { kFoo = a.E.kBar };')\n+    self.ParseMojoms([a_mojom, b_mojom])\n+    b = self.LoadModule(b_mojom)\n+\n+    self.assertEqual('F', b.enums[0].mojom_name)\n+    self.assertEqual('kFoo', b.enums[0].fields[0].mojom_name)\n+    self.assertEqual(43, b.enums[0].fields[0].numeric_value)\n+\n+  def testConstantReference(self):\n+    \"\"\"Verifies that an enum value can be assigned from the value of an\n+    integral constant within the same module.\"\"\"\n+    types = self.ExtractTypes('const int32 kFoo = 42; enum E { kA = kFoo };')\n+    self.assertEqual(42, types['E'].fields[0].numeric_value)\n+\n+  def testInvalidConstantReference(self):\n+    \"\"\"Verifies that enum values cannot be assigned from the value of\n+    non-integral constants.\"\"\"\n+    with self.assertRaisesRegexp(ValueError, 'not an integer'):\n+      self.ExtractTypes('const float kFoo = 1.0; enum E { kA = kFoo };')\n+    with self.assertRaisesRegexp(ValueError, 'not an integer'):\n+      self.ExtractTypes('const double kFoo = 1.0; enum E { kA = kFoo };')\n+    with self.assertRaisesRegexp(ValueError, 'not an integer'):\n+      self.ExtractTypes('const string kFoo = \"lol\"; enum E { kA = kFoo };')\n+\n+  def testImportedConstantReference(self):\n+    \"\"\"Verifies that an enum value can be assigned from the value of an integral\n+    constant within an imported module.\"\"\"\n+    a_mojom = 'a.mojom'\n+    self.WriteFile(a_mojom, 'module a; const int32 kFoo = 37;')\n+    b_mojom = 'b.mojom'\n+    self.WriteFile(b_mojom,\n+                   'module b; import \"a.mojom\"; enum F { kFoo = a.kFoo };')\n+    self.ParseMojoms([a_mojom, b_mojom])\n+    b = self.LoadModule(b_mojom)\n+\n+    self.assertEqual('F', b.enums[0].mojom_name)\n+    self.assertEqual('kFoo', b.enums[0].fields[0].mojom_name)\n+    self.assertEqual(37, b.enums[0].fields[0].numeric_value)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\nnew file mode 100644\nindex 00000000..7416ef19\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\n@@ -0,0 +1,43 @@\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+group(\"mojom\") {\n+  data = [\n+    \"__init__.py\",\n+    \"error.py\",\n+    \"fileutil.py\",\n+    \"generate/__init__.py\",\n+    \"generate/constant_resolver.py\",\n+    \"generate/generator.py\",\n+    \"generate/module.py\",\n+    \"generate/pack.py\",\n+    \"generate/template_expander.py\",\n+    \"generate/translate.py\",\n+    \"parse/__init__.py\",\n+    \"parse/ast.py\",\n+    \"parse/conditional_features.py\",\n+    \"parse/lexer.py\",\n+    \"parse/parser.py\",\n+\n+    # Third-party module dependencies\n+    \"//third_party/jinja2/\",\n+    \"//third_party/ply/\",\n+  ]\n+}\n+\n+group(\"tests\") {\n+  data = [\n+    \"fileutil_unittest.py\",\n+    \"generate/generator_unittest.py\",\n+    \"generate/module_unittest.py\",\n+    \"generate/pack_unittest.py\",\n+    \"generate/translate_unittest.py\",\n+    \"parse/ast_unittest.py\",\n+    \"parse/conditional_features_unittest.py\",\n+    \"parse/lexer_unittest.py\",\n+    \"parse/parser_unittest.py\",\n+  ]\n+\n+  public_deps = [ \":mojom\" ]\n+}\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/__init__.py b/utils/ipc/mojo/public/tools/mojom/mojom/__init__.py\nnew file mode 100644\nindex 00000000..e69de29b\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/error.py b/utils/ipc/mojo/public/tools/mojom/mojom/error.py\nnew file mode 100644\nindex 00000000..8a1e03da\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/error.py\n@@ -0,0 +1,28 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+\n+class Error(Exception):\n+  \"\"\"Base class for Mojo IDL bindings parser/generator errors.\"\"\"\n+\n+  def __init__(self, filename, message, lineno=None, addenda=None, **kwargs):\n+    \"\"\"|filename| is the (primary) file which caused the error, |message| is the\n+    error message, |lineno| is the 1-based line number (or |None| if not\n+    applicable/available), and |addenda| is a list of additional lines to append\n+    to the final error message.\"\"\"\n+    Exception.__init__(self, **kwargs)\n+    self.filename = filename\n+    self.message = message\n+    self.lineno = lineno\n+    self.addenda = addenda\n+\n+  def __str__(self):\n+    if self.lineno:\n+      s = \"%s:%d: Error: %s\" % (self.filename, self.lineno, self.message)\n+    else:\n+      s = \"%s: Error: %s\" % (self.filename, self.message)\n+    return \"\\n\".join([s] + self.addenda) if self.addenda else s\n+\n+  def __repr__(self):\n+    return str(self)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py\nnew file mode 100644\nindex 00000000..bf626f54\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py\n@@ -0,0 +1,45 @@\n+# Copyright 2015 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import errno\n+import imp\n+import os.path\n+import sys\n+\n+\n+def _GetDirAbove(dirname):\n+  \"\"\"Returns the directory \"above\" this file containing |dirname| (which must\n+  also be \"above\" this file).\"\"\"\n+  path = os.path.abspath(__file__)\n+  while True:\n+    path, tail = os.path.split(path)\n+    if not tail:\n+      return None\n+    if tail == dirname:\n+      return path\n+\n+\n+def EnsureDirectoryExists(path, always_try_to_create=False):\n+  \"\"\"A wrapper for os.makedirs that does not error if the directory already\n+  exists. A different process could be racing to create this directory.\"\"\"\n+\n+  if not os.path.exists(path) or always_try_to_create:\n+    try:\n+      os.makedirs(path)\n+    except OSError as e:\n+      # There may have been a race to create this directory.\n+      if e.errno != errno.EEXIST:\n+        raise\n+\n+\n+def AddLocalRepoThirdPartyDirToModulePath():\n+  \"\"\"Helper function to find the top-level directory of this script's repository\n+  assuming the script falls somewhere within a 'mojo' directory, and insert the\n+  top-level 'third_party' directory early in the module search path. Used to\n+  ensure that third-party dependencies provided within the repository itself\n+  (e.g. Chromium sources include snapshots of jinja2 and ply) are preferred over\n+  locally installed system library packages.\"\"\"\n+  toplevel_dir = _GetDirAbove('mojo')\n+  if toplevel_dir:\n+    sys.path.insert(1, os.path.join(toplevel_dir, 'third_party'))\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py\nnew file mode 100644\nindex 00000000..ff5753a2\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py\n@@ -0,0 +1,40 @@\n+# Copyright 2015 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import imp\n+import os.path\n+import shutil\n+import sys\n+import tempfile\n+import unittest\n+\n+from mojom import fileutil\n+\n+\n+class FileUtilTest(unittest.TestCase):\n+  def testEnsureDirectoryExists(self):\n+    \"\"\"Test that EnsureDirectoryExists fuctions correctly.\"\"\"\n+\n+    temp_dir = tempfile.mkdtemp()\n+    try:\n+      self.assertTrue(os.path.exists(temp_dir))\n+\n+      # Directory does not exist, yet.\n+      full = os.path.join(temp_dir, \"foo\", \"bar\")\n+      self.assertFalse(os.path.exists(full))\n+\n+      # Create the directory.\n+      fileutil.EnsureDirectoryExists(full)\n+      self.assertTrue(os.path.exists(full))\n+\n+      # Trying to create it again does not cause an error.\n+      fileutil.EnsureDirectoryExists(full)\n+      self.assertTrue(os.path.exists(full))\n+\n+      # Bypass check for directory existence to tickle error handling that\n+      # occurs in response to a race.\n+      fileutil.EnsureDirectoryExists(full, always_try_to_create=True)\n+      self.assertTrue(os.path.exists(full))\n+    finally:\n+      shutil.rmtree(temp_dir)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/__init__.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/__init__.py\nnew file mode 100644\nindex 00000000..e69de29b\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/constant_resolver.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/constant_resolver.py\nnew file mode 100644\nindex 00000000..0dfd996e\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/constant_resolver.py\n@@ -0,0 +1,93 @@\n+# Copyright 2015 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Resolves the values used for constants and enums.\"\"\"\n+\n+from itertools import ifilter\n+\n+from mojom.generate import module as mojom\n+\n+\n+def ResolveConstants(module, expression_to_text):\n+  in_progress = set()\n+  computed = set()\n+\n+  def GetResolvedValue(named_value):\n+    assert isinstance(named_value, (mojom.EnumValue, mojom.ConstantValue))\n+    if isinstance(named_value, mojom.EnumValue):\n+      field = next(\n+          ifilter(lambda field: field.name == named_value.name,\n+                  named_value.enum.fields), None)\n+      if not field:\n+        raise RuntimeError(\n+            'Unable to get computed value for field %s of enum %s' %\n+            (named_value.name, named_value.enum.name))\n+      if field not in computed:\n+        ResolveEnum(named_value.enum)\n+      return field.resolved_value\n+    else:\n+      ResolveConstant(named_value.constant)\n+      named_value.resolved_value = named_value.constant.resolved_value\n+      return named_value.resolved_value\n+\n+  def ResolveConstant(constant):\n+    if constant in computed:\n+      return\n+    if constant in in_progress:\n+      raise RuntimeError('Circular dependency for constant: %s' % constant.name)\n+    in_progress.add(constant)\n+    if isinstance(constant.value, (mojom.EnumValue, mojom.ConstantValue)):\n+      resolved_value = GetResolvedValue(constant.value)\n+    else:\n+      resolved_value = expression_to_text(constant.value)\n+    constant.resolved_value = resolved_value\n+    in_progress.remove(constant)\n+    computed.add(constant)\n+\n+  def ResolveEnum(enum):\n+    def ResolveEnumField(enum, field, default_value):\n+      if field in computed:\n+        return\n+      if field in in_progress:\n+        raise RuntimeError('Circular dependency for enum: %s' % enum.name)\n+      in_progress.add(field)\n+      if field.value:\n+        if isinstance(field.value, mojom.EnumValue):\n+          resolved_value = GetResolvedValue(field.value)\n+        elif isinstance(field.value, str):\n+          resolved_value = int(field.value, 0)\n+        else:\n+          raise RuntimeError('Unexpected value: %s' % field.value)\n+      else:\n+        resolved_value = default_value\n+      field.resolved_value = resolved_value\n+      in_progress.remove(field)\n+      computed.add(field)\n+\n+    current_value = 0\n+    for field in enum.fields:\n+      ResolveEnumField(enum, field, current_value)\n+      current_value = field.resolved_value + 1\n+\n+  for constant in module.constants:\n+    ResolveConstant(constant)\n+\n+  for enum in module.enums:\n+    ResolveEnum(enum)\n+\n+  for struct in module.structs:\n+    for constant in struct.constants:\n+      ResolveConstant(constant)\n+    for enum in struct.enums:\n+      ResolveEnum(enum)\n+    for field in struct.fields:\n+      if isinstance(field.default, (mojom.ConstantValue, mojom.EnumValue)):\n+        field.default.resolved_value = GetResolvedValue(field.default)\n+\n+  for interface in module.interfaces:\n+    for constant in interface.constants:\n+      ResolveConstant(constant)\n+    for enum in interface.enums:\n+      ResolveEnum(enum)\n+\n+  return module\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\nnew file mode 100644\nindex 00000000..de62260a\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\n@@ -0,0 +1,325 @@\n+# Copyright 2013 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Code shared by the various language-specific code generators.\"\"\"\n+\n+from __future__ import print_function\n+\n+from functools import partial\n+import os.path\n+import re\n+\n+from mojom import fileutil\n+from mojom.generate import module as mojom\n+from mojom.generate import pack\n+\n+\n+def ExpectedArraySize(kind):\n+  if mojom.IsArrayKind(kind):\n+    return kind.length\n+  return None\n+\n+\n+def SplitCamelCase(identifier):\n+  \"\"\"Splits a camel-cased |identifier| and returns a list of lower-cased\n+  strings.\n+  \"\"\"\n+  # Add underscores after uppercase letters when appropriate. An uppercase\n+  # letter is considered the end of a word if it is followed by an upper and a\n+  # lower. E.g. URLLoaderFactory -> URL_LoaderFactory\n+  identifier = re.sub('([A-Z][0-9]*)(?=[A-Z][0-9]*[a-z])', r'\\1_', identifier)\n+  # Add underscores after lowercase letters when appropriate. A lowercase letter\n+  # is considered the end of a word if it is followed by an upper.\n+  # E.g. URLLoaderFactory -> URLLoader_Factory\n+  identifier = re.sub('([a-z][0-9]*)(?=[A-Z])', r'\\1_', identifier)\n+  return [x.lower() for x in identifier.split('_')]\n+\n+\n+def ToCamel(identifier, lower_initial=False, digits_split=False, delimiter='_'):\n+  \"\"\"Splits |identifier| using |delimiter|, makes the first character of each\n+  word uppercased (but makes the first character of the first word lowercased\n+  if |lower_initial| is set to True), and joins the words. Please note that for\n+  each word, all the characters except the first one are untouched.\n+  \"\"\"\n+  result = ''\n+  capitalize_next = True\n+  for i in range(len(identifier)):\n+    if identifier[i] == delimiter:\n+      capitalize_next = True\n+    elif digits_split and identifier[i].isdigit():\n+      capitalize_next = True\n+      result += identifier[i]\n+    elif capitalize_next:\n+      capitalize_next = False\n+      result += identifier[i].upper()\n+    else:\n+      result += identifier[i]\n+\n+  if lower_initial and result:\n+    result = result[0].lower() + result[1:]\n+\n+  return result\n+\n+\n+def _ToSnakeCase(identifier, upper=False):\n+  \"\"\"Splits camel-cased |identifier| into lower case words, removes the first\n+  word if it's \"k\" and joins them using \"_\" e.g. for \"URLLoaderFactory\", returns\n+  \"URL_LOADER_FACTORY\" if upper, otherwise \"url_loader_factory\".\n+  \"\"\"\n+  words = SplitCamelCase(identifier)\n+  if words[0] == 'k' and len(words) > 1:\n+    words = words[1:]\n+\n+  # Variables cannot start with a digit\n+  if (words[0][0].isdigit()):\n+    words[0] = '_' + words[0]\n+\n+\n+  if upper:\n+    words = map(lambda x: x.upper(), words)\n+\n+  return '_'.join(words)\n+\n+\n+def ToUpperSnakeCase(identifier):\n+  \"\"\"Splits camel-cased |identifier| into lower case words, removes the first\n+  word if it's \"k\" and joins them using \"_\" e.g. for \"URLLoaderFactory\", returns\n+  \"URL_LOADER_FACTORY\".\n+  \"\"\"\n+  return _ToSnakeCase(identifier, upper=True)\n+\n+\n+def ToLowerSnakeCase(identifier):\n+  \"\"\"Splits camel-cased |identifier| into lower case words, removes the first\n+  word if it's \"k\" and joins them using \"_\" e.g. for \"URLLoaderFactory\", returns\n+  \"url_loader_factory\".\n+  \"\"\"\n+  return _ToSnakeCase(identifier, upper=False)\n+\n+\n+class Stylizer(object):\n+  \"\"\"Stylizers specify naming rules to map mojom names to names in generated\n+  code. For example, if you would like method_name in mojom to be mapped to\n+  MethodName in the generated code, you need to define a subclass of Stylizer\n+  and override StylizeMethod to do the conversion.\"\"\"\n+\n+  def StylizeConstant(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeField(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeStruct(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeUnion(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeParameter(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeMethod(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeInterface(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeEnumField(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeEnum(self, mojom_name):\n+    return mojom_name\n+\n+  def StylizeModule(self, mojom_namespace):\n+    return mojom_namespace\n+\n+\n+def WriteFile(contents, full_path):\n+  # If |contents| is same with the file content, we skip updating.\n+  if os.path.isfile(full_path):\n+    with open(full_path, 'rb') as destination_file:\n+      if destination_file.read() == contents:\n+        return\n+\n+  # Make sure the containing directory exists.\n+  full_dir = os.path.dirname(full_path)\n+  fileutil.EnsureDirectoryExists(full_dir)\n+\n+  # Dump the data to disk.\n+  with open(full_path, \"wb\") as f:\n+    if not isinstance(contents, bytes):\n+      f.write(contents.encode('utf-8'))\n+    else:\n+      f.write(contents)\n+\n+\n+def AddComputedData(module):\n+  \"\"\"Adds computed data to the given module. The data is computed once and\n+  used repeatedly in the generation process.\"\"\"\n+\n+  def _AddStructComputedData(exported, struct):\n+    struct.packed = pack.PackedStruct(struct)\n+    struct.bytes = pack.GetByteLayout(struct.packed)\n+    struct.versions = pack.GetVersionInfo(struct.packed)\n+    struct.exported = exported\n+\n+  def _AddInterfaceComputedData(interface):\n+    interface.version = 0\n+    for method in interface.methods:\n+      # this field is never scrambled\n+      method.sequential_ordinal = method.ordinal\n+\n+      if method.min_version is not None:\n+        interface.version = max(interface.version, method.min_version)\n+\n+      method.param_struct = _GetStructFromMethod(method)\n+      if interface.stable:\n+        method.param_struct.attributes[mojom.ATTRIBUTE_STABLE] = True\n+        if method.explicit_ordinal is None:\n+          raise Exception(\n+              'Stable interfaces must declare explicit method ordinals. The '\n+              'method %s on stable interface %s does not declare an explicit '\n+              'ordinal.' % (method.mojom_name, interface.qualified_name))\n+      interface.version = max(interface.version,\n+                              method.param_struct.versions[-1].version)\n+\n+      if method.response_parameters is not None:\n+        method.response_param_struct = _GetResponseStructFromMethod(method)\n+        if interface.stable:\n+          method.response_param_struct.attributes[mojom.ATTRIBUTE_STABLE] = True\n+        interface.version = max(\n+            interface.version,\n+            method.response_param_struct.versions[-1].version)\n+      else:\n+        method.response_param_struct = None\n+\n+  def _GetStructFromMethod(method):\n+    \"\"\"Converts a method's parameters into the fields of a struct.\"\"\"\n+    params_class = \"%s_%s_Params\" % (method.interface.mojom_name,\n+                                     method.mojom_name)\n+    struct = mojom.Struct(params_class,\n+                          module=method.interface.module,\n+                          attributes={})\n+    for param in method.parameters:\n+      struct.AddField(\n+          param.mojom_name,\n+          param.kind,\n+          param.ordinal,\n+          attributes=param.attributes)\n+    _AddStructComputedData(False, struct)\n+    return struct\n+\n+  def _GetResponseStructFromMethod(method):\n+    \"\"\"Converts a method's response_parameters into the fields of a struct.\"\"\"\n+    params_class = \"%s_%s_ResponseParams\" % (method.interface.mojom_name,\n+                                             method.mojom_name)\n+    struct = mojom.Struct(params_class,\n+                          module=method.interface.module,\n+                          attributes={})\n+    for param in method.response_parameters:\n+      struct.AddField(\n+          param.mojom_name,\n+          param.kind,\n+          param.ordinal,\n+          attributes=param.attributes)\n+    _AddStructComputedData(False, struct)\n+    return struct\n+\n+  for struct in module.structs:\n+    _AddStructComputedData(True, struct)\n+  for interface in module.interfaces:\n+    _AddInterfaceComputedData(interface)\n+\n+\n+class Generator(object):\n+  # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all\n+  # files to stdout.\n+  def __init__(self,\n+               module,\n+               output_dir=None,\n+               typemap=None,\n+               variant=None,\n+               bytecode_path=None,\n+               for_blink=False,\n+               js_bindings_mode=\"new\",\n+               js_generate_struct_deserializers=False,\n+               export_attribute=None,\n+               export_header=None,\n+               generate_non_variant_code=False,\n+               support_lazy_serialization=False,\n+               disallow_native_types=False,\n+               disallow_interfaces=False,\n+               generate_message_ids=False,\n+               generate_fuzzing=False,\n+               enable_kythe_annotations=False,\n+               extra_cpp_template_paths=None,\n+               generate_extra_cpp_only=False):\n+    self.module = module\n+    self.output_dir = output_dir\n+    self.typemap = typemap or {}\n+    self.variant = variant\n+    self.bytecode_path = bytecode_path\n+    self.for_blink = for_blink\n+    self.js_bindings_mode = js_bindings_mode\n+    self.js_generate_struct_deserializers = js_generate_struct_deserializers\n+    self.export_attribute = export_attribute\n+    self.export_header = export_header\n+    self.generate_non_variant_code = generate_non_variant_code\n+    self.support_lazy_serialization = support_lazy_serialization\n+    self.disallow_native_types = disallow_native_types\n+    self.disallow_interfaces = disallow_interfaces\n+    self.generate_message_ids = generate_message_ids\n+    self.generate_fuzzing = generate_fuzzing\n+    self.enable_kythe_annotations = enable_kythe_annotations\n+    self.extra_cpp_template_paths = extra_cpp_template_paths\n+    self.generate_extra_cpp_only = generate_extra_cpp_only\n+\n+  def Write(self, contents, filename):\n+    if self.output_dir is None:\n+      print(contents)\n+      return\n+    full_path = os.path.join(self.output_dir, filename)\n+    WriteFile(contents, full_path)\n+\n+  def OptimizeEmpty(self, contents):\n+    # Look for .cc files that contain no actual code. There are many of these\n+    # and they collectively take a while to compile.\n+    lines = contents.splitlines()\n+\n+    for line in lines:\n+      if line.startswith('#') or line.startswith('//'):\n+        continue\n+      if re.match(r'namespace .* {', line) or re.match(r'}.*//.*namespace',\n+                                                       line):\n+        continue\n+      if line.strip():\n+        # There is some actual code - return the unmodified contents.\n+        return contents\n+\n+    # If we reach here then we have a .cc file with no actual code. The\n+    # includes are therefore unneeded and can be removed.\n+    new_lines = [line for line in lines if not line.startswith('#include')]\n+    if len(new_lines) < len(lines):\n+      new_lines.append('')\n+      new_lines.append('// Includes removed due to no code being generated.')\n+    return '\\n'.join(new_lines)\n+\n+  def WriteWithComment(self, contents, filename):\n+    generator_name = \"mojom_bindings_generator.py\"\n+    comment = r\"// %s is auto generated by %s, do not edit\" % (filename,\n+                                                               generator_name)\n+    contents = comment + '\\n' + '\\n' + contents;\n+    if filename.endswith('.cc'):\n+      contents = self.OptimizeEmpty(contents)\n+    self.Write(contents, filename)\n+\n+  def GenerateFiles(self, args):\n+    raise NotImplementedError(\"Subclasses must override/implement this method\")\n+\n+  def GetJinjaParameters(self):\n+    \"\"\"Returns default constructor parameters for the jinja environment.\"\"\"\n+    return {}\n+\n+  def GetGlobals(self):\n+    \"\"\"Returns global mappings for the template generation.\"\"\"\n+    return {}\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py\nnew file mode 100644\nindex 00000000..32c884a8\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py\n@@ -0,0 +1,74 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import imp\n+import os.path\n+import sys\n+import unittest\n+\n+\n+def _GetDirAbove(dirname):\n+  \"\"\"Returns the directory \"above\" this file containing |dirname| (which must\n+  also be \"above\" this file).\"\"\"\n+  path = os.path.abspath(__file__)\n+  while True:\n+    path, tail = os.path.split(path)\n+    assert tail\n+    if tail == dirname:\n+      return path\n+\n+\n+try:\n+  imp.find_module(\"mojom\")\n+except ImportError:\n+  sys.path.append(os.path.join(_GetDirAbove(\"pylib\"), \"pylib\"))\n+from mojom.generate import generator\n+\n+\n+class StringManipulationTest(unittest.TestCase):\n+  \"\"\"generator contains some string utilities, this tests only those.\"\"\"\n+\n+  def testSplitCamelCase(self):\n+    self.assertEquals([\"camel\", \"case\"], generator.SplitCamelCase(\"CamelCase\"))\n+    self.assertEquals([\"url\", \"loader\", \"factory\"],\n+                      generator.SplitCamelCase('URLLoaderFactory'))\n+    self.assertEquals([\"get99\", \"entries\"],\n+                      generator.SplitCamelCase('Get99Entries'))\n+    self.assertEquals([\"get99entries\"],\n+                      generator.SplitCamelCase('Get99entries'))\n+\n+  def testToCamel(self):\n+    self.assertEquals(\"CamelCase\", generator.ToCamel(\"camel_case\"))\n+    self.assertEquals(\"CAMELCASE\", generator.ToCamel(\"CAMEL_CASE\"))\n+    self.assertEquals(\"camelCase\",\n+                      generator.ToCamel(\"camel_case\", lower_initial=True))\n+    self.assertEquals(\"CamelCase\", generator.ToCamel(\n+        \"camel case\", delimiter=' '))\n+    self.assertEquals(\"CaMelCaSe\", generator.ToCamel(\"caMel_caSe\"))\n+    self.assertEquals(\"L2Tp\", generator.ToCamel(\"l2tp\", digits_split=True))\n+    self.assertEquals(\"l2tp\", generator.ToCamel(\"l2tp\", lower_initial=True))\n+\n+  def testToSnakeCase(self):\n+    self.assertEquals(\"snake_case\", generator.ToLowerSnakeCase(\"SnakeCase\"))\n+    self.assertEquals(\"snake_case\", generator.ToLowerSnakeCase(\"snakeCase\"))\n+    self.assertEquals(\"snake_case\", generator.ToLowerSnakeCase(\"SnakeCASE\"))\n+    self.assertEquals(\"snake_d3d11_case\",\n+                      generator.ToLowerSnakeCase(\"SnakeD3D11Case\"))\n+    self.assertEquals(\"snake_d3d11_case\",\n+                      generator.ToLowerSnakeCase(\"SnakeD3d11Case\"))\n+    self.assertEquals(\"snake_d3d11_case\",\n+                      generator.ToLowerSnakeCase(\"snakeD3d11Case\"))\n+    self.assertEquals(\"SNAKE_CASE\", generator.ToUpperSnakeCase(\"SnakeCase\"))\n+    self.assertEquals(\"SNAKE_CASE\", generator.ToUpperSnakeCase(\"snakeCase\"))\n+    self.assertEquals(\"SNAKE_CASE\", generator.ToUpperSnakeCase(\"SnakeCASE\"))\n+    self.assertEquals(\"SNAKE_D3D11_CASE\",\n+                      generator.ToUpperSnakeCase(\"SnakeD3D11Case\"))\n+    self.assertEquals(\"SNAKE_D3D11_CASE\",\n+                      generator.ToUpperSnakeCase(\"SnakeD3d11Case\"))\n+    self.assertEquals(\"SNAKE_D3D11_CASE\",\n+                      generator.ToUpperSnakeCase(\"snakeD3d11Case\"))\n+\n+\n+if __name__ == \"__main__\":\n+  unittest.main()\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\nnew file mode 100644\nindex 00000000..8547ff64\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\n@@ -0,0 +1,1635 @@\n+# Copyright 2013 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+# This module's classes provide an interface to mojo modules. Modules are\n+# collections of interfaces and structs to be used by mojo ipc clients and\n+# servers.\n+#\n+# A simple interface would be created this way:\n+# module = mojom.generate.module.Module('Foo')\n+# interface = module.AddInterface('Bar')\n+# method = interface.AddMethod('Tat', 0)\n+# method.AddParameter('baz', 0, mojom.INT32)\n+\n+import pickle\n+\n+# We use our own version of __repr__ when displaying the AST, as the\n+# AST currently doesn't capture which nodes are reference (e.g. to\n+# types) and which nodes are definitions. This allows us to e.g. print\n+# the definition of a struct when it's defined inside a module, but\n+# only print its name when it's referenced in e.g. a method parameter.\n+def Repr(obj, as_ref=True):\n+  \"\"\"A version of __repr__ that can distinguish references.\n+\n+  Sometimes we like to print an object's full representation\n+  (e.g. with its fields) and sometimes we just want to reference an\n+  object that was printed in full elsewhere. This function allows us\n+  to make that distinction.\n+\n+  Args:\n+    obj: The object whose string representation we compute.\n+    as_ref: If True, use the short reference representation.\n+\n+  Returns:\n+    A str representation of |obj|.\n+  \"\"\"\n+  if hasattr(obj, 'Repr'):\n+    return obj.Repr(as_ref=as_ref)\n+  # Since we cannot implement Repr for existing container types, we\n+  # handle them here.\n+  elif isinstance(obj, list):\n+    if not obj:\n+      return '[]'\n+    else:\n+      return ('[\\n%s\\n]' % (',\\n'.join(\n+          '    %s' % Repr(elem, as_ref).replace('\\n', '\\n    ')\n+          for elem in obj)))\n+  elif isinstance(obj, dict):\n+    if not obj:\n+      return '{}'\n+    else:\n+      return ('{\\n%s\\n}' % (',\\n'.join(\n+          '    %s: %s' % (Repr(key, as_ref).replace('\\n', '\\n    '),\n+                          Repr(val, as_ref).replace('\\n', '\\n    '))\n+          for key, val in obj.items())))\n+  else:\n+    return repr(obj)\n+\n+\n+def GenericRepr(obj, names):\n+  \"\"\"Compute generic Repr for |obj| based on the attributes in |names|.\n+\n+  Args:\n+    obj: The object to compute a Repr for.\n+    names: A dict from attribute names to include, to booleans\n+        specifying whether those attributes should be shown as\n+        references or not.\n+\n+  Returns:\n+    A str representation of |obj|.\n+  \"\"\"\n+\n+  def ReprIndent(name, as_ref):\n+    return '    %s=%s' % (name, Repr(getattr(obj, name), as_ref).replace(\n+        '\\n', '\\n    '))\n+\n+  return '%s(\\n%s\\n)' % (obj.__class__.__name__, ',\\n'.join(\n+      ReprIndent(name, as_ref) for (name, as_ref) in names.items()))\n+\n+\n+class Kind(object):\n+  \"\"\"Kind represents a type (e.g. int8, string).\n+\n+  Attributes:\n+    spec: A string uniquely identifying the type. May be None.\n+    module: {Module} The defining module. Set to None for built-in types.\n+    parent_kind: The enclosing type. For example, an enum defined\n+        inside an interface has that interface as its parent. May be None.\n+  \"\"\"\n+\n+  def __init__(self, spec=None, module=None):\n+    self.spec = spec\n+    self.module = module\n+    self.parent_kind = None\n+\n+  def Repr(self, as_ref=True):\n+    # pylint: disable=unused-argument\n+    return '<%s spec=%r>' % (self.__class__.__name__, self.spec)\n+\n+  def __repr__(self):\n+    # Gives us a decent __repr__ for all kinds.\n+    return self.Repr()\n+\n+  def __eq__(self, rhs):\n+    # pylint: disable=unidiomatic-typecheck\n+    return (type(self) == type(rhs)\n+            and (self.spec, self.parent_kind) == (rhs.spec, rhs.parent_kind))\n+\n+  def __hash__(self):\n+    # TODO(crbug.com/1060471): Remove this and other __hash__ methods on Kind\n+    # and its subclasses. This is to support existing generator code which uses\n+    # some primitive Kinds as dict keys. The default hash (object identity)\n+    # breaks these dicts when a pickled Module instance is unpickled and used\n+    # during a subsequent run of the parser.\n+    return hash((self.spec, self.parent_kind))\n+\n+\n+class ReferenceKind(Kind):\n+  \"\"\"ReferenceKind represents pointer and handle types.\n+\n+  A type is nullable if null (for pointer types) or invalid handle (for handle\n+  types) is a legal value for the type.\n+\n+  Attributes:\n+    is_nullable: True if the type is nullable.\n+  \"\"\"\n+\n+  def __init__(self, spec=None, is_nullable=False, module=None):\n+    assert spec is None or is_nullable == spec.startswith('?')\n+    Kind.__init__(self, spec, module)\n+    self.is_nullable = is_nullable\n+    self.shared_definition = {}\n+\n+  def Repr(self, as_ref=True):\n+    return '<%s spec=%r is_nullable=%r>' % (self.__class__.__name__, self.spec,\n+                                            self.is_nullable)\n+\n+  def MakeNullableKind(self):\n+    assert not self.is_nullable\n+\n+    if self == STRING:\n+      return NULLABLE_STRING\n+    if self == HANDLE:\n+      return NULLABLE_HANDLE\n+    if self == DCPIPE:\n+      return NULLABLE_DCPIPE\n+    if self == DPPIPE:\n+      return NULLABLE_DPPIPE\n+    if self == MSGPIPE:\n+      return NULLABLE_MSGPIPE\n+    if self == SHAREDBUFFER:\n+      return NULLABLE_SHAREDBUFFER\n+    if self == PLATFORMHANDLE:\n+      return NULLABLE_PLATFORMHANDLE\n+\n+    nullable_kind = type(self)()\n+    nullable_kind.shared_definition = self.shared_definition\n+    if self.spec is not None:\n+      nullable_kind.spec = '?' + self.spec\n+    nullable_kind.is_nullable = True\n+    nullable_kind.parent_kind = self.parent_kind\n+    nullable_kind.module = self.module\n+\n+    return nullable_kind\n+\n+  @classmethod\n+  def AddSharedProperty(cls, name):\n+    \"\"\"Adds a property |name| to |cls|, which accesses the corresponding item in\n+       |shared_definition|.\n+\n+       The reason of adding such indirection is to enable sharing definition\n+       between a reference kind and its nullable variation. For example:\n+         a = Struct('test_struct_1')\n+         b = a.MakeNullableKind()\n+         a.name = 'test_struct_2'\n+         print(b.name)  # Outputs 'test_struct_2'.\n+    \"\"\"\n+\n+    def Get(self):\n+      try:\n+        return self.shared_definition[name]\n+      except KeyError:  # Must raise AttributeError if property doesn't exist.\n+        raise AttributeError\n+\n+    def Set(self, value):\n+      self.shared_definition[name] = value\n+\n+    setattr(cls, name, property(Get, Set))\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, ReferenceKind)\n+            and super(ReferenceKind, self).__eq__(rhs)\n+            and self.is_nullable == rhs.is_nullable)\n+\n+  def __hash__(self):\n+    return hash((super(ReferenceKind, self).__hash__(), self.is_nullable))\n+\n+\n+# Initialize the set of primitive types. These can be accessed by clients.\n+BOOL = Kind('b')\n+INT8 = Kind('i8')\n+INT16 = Kind('i16')\n+INT32 = Kind('i32')\n+INT64 = Kind('i64')\n+UINT8 = Kind('u8')\n+UINT16 = Kind('u16')\n+UINT32 = Kind('u32')\n+UINT64 = Kind('u64')\n+FLOAT = Kind('f')\n+DOUBLE = Kind('d')\n+STRING = ReferenceKind('s')\n+HANDLE = ReferenceKind('h')\n+DCPIPE = ReferenceKind('h:d:c')\n+DPPIPE = ReferenceKind('h:d:p')\n+MSGPIPE = ReferenceKind('h:m')\n+SHAREDBUFFER = ReferenceKind('h:s')\n+PLATFORMHANDLE = ReferenceKind('h:p')\n+NULLABLE_STRING = ReferenceKind('?s', True)\n+NULLABLE_HANDLE = ReferenceKind('?h', True)\n+NULLABLE_DCPIPE = ReferenceKind('?h:d:c', True)\n+NULLABLE_DPPIPE = ReferenceKind('?h:d:p', True)\n+NULLABLE_MSGPIPE = ReferenceKind('?h:m', True)\n+NULLABLE_SHAREDBUFFER = ReferenceKind('?h:s', True)\n+NULLABLE_PLATFORMHANDLE = ReferenceKind('?h:p', True)\n+\n+# Collection of all Primitive types\n+PRIMITIVES = (\n+    BOOL,\n+    INT8,\n+    INT16,\n+    INT32,\n+    INT64,\n+    UINT8,\n+    UINT16,\n+    UINT32,\n+    UINT64,\n+    FLOAT,\n+    DOUBLE,\n+    STRING,\n+    HANDLE,\n+    DCPIPE,\n+    DPPIPE,\n+    MSGPIPE,\n+    SHAREDBUFFER,\n+    PLATFORMHANDLE,\n+    NULLABLE_STRING,\n+    NULLABLE_HANDLE,\n+    NULLABLE_DCPIPE,\n+    NULLABLE_DPPIPE,\n+    NULLABLE_MSGPIPE,\n+    NULLABLE_SHAREDBUFFER,\n+    NULLABLE_PLATFORMHANDLE,\n+)\n+\n+ATTRIBUTE_MIN_VERSION = 'MinVersion'\n+ATTRIBUTE_EXTENSIBLE = 'Extensible'\n+ATTRIBUTE_STABLE = 'Stable'\n+ATTRIBUTE_SYNC = 'Sync'\n+\n+\n+class NamedValue(object):\n+  def __init__(self, module, parent_kind, mojom_name):\n+    self.module = module\n+    self.parent_kind = parent_kind\n+    self.mojom_name = mojom_name\n+\n+  def GetSpec(self):\n+    return (self.module.GetNamespacePrefix() +\n+            (self.parent_kind and\n+             (self.parent_kind.mojom_name + '.') or \"\") + self.mojom_name)\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, NamedValue)\n+            and (self.parent_kind, self.mojom_name) == (rhs.parent_kind,\n+                                                        rhs.mojom_name))\n+\n+\n+class BuiltinValue(object):\n+  def __init__(self, value):\n+    self.value = value\n+\n+  def __eq__(self, rhs):\n+    return isinstance(rhs, BuiltinValue) and self.value == rhs.value\n+\n+\n+class ConstantValue(NamedValue):\n+  def __init__(self, module, parent_kind, constant):\n+    NamedValue.__init__(self, module, parent_kind, constant.mojom_name)\n+    self.constant = constant\n+\n+  @property\n+  def name(self):\n+    return self.constant.name\n+\n+\n+class EnumValue(NamedValue):\n+  def __init__(self, module, enum, field):\n+    NamedValue.__init__(self, module, enum.parent_kind, field.mojom_name)\n+    self.field = field\n+    self.enum = enum\n+\n+  def GetSpec(self):\n+    return (self.module.GetNamespacePrefix() +\n+            (self.parent_kind and (self.parent_kind.mojom_name + '.') or \"\") +\n+            self.enum.mojom_name + '.' + self.mojom_name)\n+\n+  @property\n+  def name(self):\n+    return self.field.name\n+\n+\n+class Constant(object):\n+  def __init__(self, mojom_name=None, kind=None, value=None, parent_kind=None):\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.kind = kind\n+    self.value = value\n+    self.parent_kind = parent_kind\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeConstant(self.mojom_name)\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Constant)\n+            and (self.mojom_name, self.kind, self.value,\n+                 self.parent_kind) == (rhs.mojom_name, rhs.kind, rhs.value,\n+                                       rhs.parent_kind))\n+\n+\n+class Field(object):\n+  def __init__(self,\n+               mojom_name=None,\n+               kind=None,\n+               ordinal=None,\n+               default=None,\n+               attributes=None):\n+    if self.__class__.__name__ == 'Field':\n+      raise Exception()\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.kind = kind\n+    self.ordinal = ordinal\n+    self.default = default\n+    self.attributes = attributes\n+\n+  def Repr(self, as_ref=True):\n+    # pylint: disable=unused-argument\n+    # Fields are only referenced by objects which define them and thus\n+    # they are always displayed as non-references.\n+    return GenericRepr(self, {'mojom_name': False, 'kind': True})\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeField(self.mojom_name)\n+\n+  @property\n+  def min_version(self):\n+    return self.attributes.get(ATTRIBUTE_MIN_VERSION) \\\n+        if self.attributes else None\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Field)\n+            and (self.mojom_name, self.kind, self.ordinal, self.default,\n+                 self.attributes) == (rhs.mojom_name, rhs.kind, rhs.ordinal,\n+                                      rhs.default, rhs.attributes))\n+\n+  def __hash__(self):\n+    return hash((self.mojom_name, self.kind, self.ordinal, self.default))\n+\n+\n+class StructField(Field):\n+  pass\n+\n+\n+class UnionField(Field):\n+  pass\n+\n+\n+def _IsFieldBackwardCompatible(new_field, old_field):\n+  if (new_field.min_version or 0) != (old_field.min_version or 0):\n+    return False\n+\n+  if isinstance(new_field.kind, (Enum, Struct, Union)):\n+    return new_field.kind.IsBackwardCompatible(old_field.kind)\n+\n+  return new_field.kind == old_field.kind\n+\n+\n+class Struct(ReferenceKind):\n+  \"\"\"A struct with typed fields.\n+\n+  Attributes:\n+    mojom_name: {str} The name of the struct type as defined in mojom.\n+    name: {str} The stylized name.\n+    native_only: {bool} Does the struct have a body (i.e. any fields) or is it\n+        purely a native struct.\n+    custom_serializer: {bool} Should we generate a serializer for the struct or\n+        will one be provided by non-generated code.\n+    fields: {List[StructField]} The members of the struct.\n+    enums: {List[Enum]} The enums defined in the struct scope.\n+    constants: {List[Constant]} The constants defined in the struct scope.\n+    attributes: {dict} Additional information about the struct, such as\n+        if it's a native struct.\n+  \"\"\"\n+\n+  ReferenceKind.AddSharedProperty('mojom_name')\n+  ReferenceKind.AddSharedProperty('name')\n+  ReferenceKind.AddSharedProperty('native_only')\n+  ReferenceKind.AddSharedProperty('custom_serializer')\n+  ReferenceKind.AddSharedProperty('fields')\n+  ReferenceKind.AddSharedProperty('enums')\n+  ReferenceKind.AddSharedProperty('constants')\n+  ReferenceKind.AddSharedProperty('attributes')\n+\n+  def __init__(self, mojom_name=None, module=None, attributes=None):\n+    if mojom_name is not None:\n+      spec = 'x:' + mojom_name\n+    else:\n+      spec = None\n+    ReferenceKind.__init__(self, spec, False, module)\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.native_only = False\n+    self.custom_serializer = False\n+    self.fields = []\n+    self.enums = []\n+    self.constants = []\n+    self.attributes = attributes\n+\n+  def Repr(self, as_ref=True):\n+    if as_ref:\n+      return '<%s mojom_name=%r module=%s>' % (self.__class__.__name__,\n+                                               self.mojom_name,\n+                                               Repr(self.module, as_ref=True))\n+    else:\n+      return GenericRepr(self, {\n+          'mojom_name': False,\n+          'fields': False,\n+          'module': True\n+      })\n+\n+  def AddField(self,\n+               mojom_name,\n+               kind,\n+               ordinal=None,\n+               default=None,\n+               attributes=None):\n+    field = StructField(mojom_name, kind, ordinal, default, attributes)\n+    self.fields.append(field)\n+    return field\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeStruct(self.mojom_name)\n+    for field in self.fields:\n+      field.Stylize(stylizer)\n+    for enum in self.enums:\n+      enum.Stylize(stylizer)\n+    for constant in self.constants:\n+      constant.Stylize(stylizer)\n+\n+  def IsBackwardCompatible(self, older_struct):\n+    \"\"\"This struct is backward-compatible with older_struct if and only if all\n+    of the following conditions hold:\n+      - Any newly added field is tagged with a [MinVersion] attribute specifying\n+        a version number greater than all previously used [MinVersion]\n+        attributes within the struct.\n+      - All fields present in older_struct remain present in the new struct,\n+        with the same ordinal position, same optional or non-optional status,\n+        same (or backward-compatible) type and where applicable, the same\n+        [MinVersion] attribute value.\n+      - All [MinVersion] attributes must be non-decreasing in ordinal order.\n+      - All reference-typed (string, array, map, struct, or union) fields tagged\n+        with a [MinVersion] greater than zero must be optional.\n+    \"\"\"\n+\n+    def buildOrdinalFieldMap(struct):\n+      fields_by_ordinal = {}\n+      for field in struct.fields:\n+        if field.ordinal in fields_by_ordinal:\n+          raise Exception('Multiple fields with ordinal %s in struct %s.' %\n+                          (field.ordinal, struct.mojom_name))\n+        fields_by_ordinal[field.ordinal] = field\n+      return fields_by_ordinal\n+\n+    new_fields = buildOrdinalFieldMap(self)\n+    old_fields = buildOrdinalFieldMap(older_struct)\n+    if len(new_fields) < len(old_fields):\n+      # At least one field was removed, which is not OK.\n+      return False\n+\n+    # If there are N fields, existing ordinal values must exactly cover the\n+    # range from 0 to N-1.\n+    num_old_ordinals = len(old_fields)\n+    max_old_min_version = 0\n+    for ordinal in range(num_old_ordinals):\n+      new_field = new_fields[ordinal]\n+      old_field = old_fields[ordinal]\n+      if (old_field.min_version or 0) > max_old_min_version:\n+        max_old_min_version = old_field.min_version\n+      if not _IsFieldBackwardCompatible(new_field, old_field):\n+        # Type or min-version mismatch between old and new versions of the same\n+        # ordinal field.\n+        return False\n+\n+    # At this point we know all old fields are intact in the new struct\n+    # definition. Now verify that all new fields have a high enough min version\n+    # and are appropriately optional where required.\n+    num_new_ordinals = len(new_fields)\n+    last_min_version = max_old_min_version\n+    for ordinal in range(num_old_ordinals, num_new_ordinals):\n+      new_field = new_fields[ordinal]\n+      min_version = new_field.min_version or 0\n+      if min_version <= max_old_min_version:\n+        # A new field is being added to an existing version, which is not OK.\n+        return False\n+      if min_version < last_min_version:\n+        # The [MinVersion] of a field cannot be lower than the [MinVersion] of\n+        # a field with lower ordinal value.\n+        return False\n+      if IsReferenceKind(new_field.kind) and not IsNullableKind(new_field.kind):\n+        # New fields whose type can be nullable MUST be nullable.\n+        return False\n+\n+    return True\n+\n+  @property\n+  def stable(self):\n+    return self.attributes.get(ATTRIBUTE_STABLE, False) \\\n+        if self.attributes else False\n+\n+  @property\n+  def qualified_name(self):\n+    if self.parent_kind:\n+      prefix = self.parent_kind.qualified_name + '.'\n+    else:\n+      prefix = self.module.GetNamespacePrefix()\n+    return '%s%s' % (prefix, self.mojom_name)\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Struct) and\n+            (self.mojom_name, self.native_only, self.fields, self.constants,\n+             self.attributes) == (rhs.mojom_name, rhs.native_only, rhs.fields,\n+                                  rhs.constants, rhs.attributes))\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class Union(ReferenceKind):\n+  \"\"\"A union of several kinds.\n+\n+  Attributes:\n+    mojom_name: {str} The name of the union type as defined in mojom.\n+    name: {str} The stylized name.\n+    fields: {List[UnionField]} The members of the union.\n+    attributes: {dict} Additional information about the union, such as\n+        which Java class name to use to represent it in the generated\n+        bindings.\n+  \"\"\"\n+  ReferenceKind.AddSharedProperty('mojom_name')\n+  ReferenceKind.AddSharedProperty('name')\n+  ReferenceKind.AddSharedProperty('fields')\n+  ReferenceKind.AddSharedProperty('attributes')\n+\n+  def __init__(self, mojom_name=None, module=None, attributes=None):\n+    if mojom_name is not None:\n+      spec = 'x:' + mojom_name\n+    else:\n+      spec = None\n+    ReferenceKind.__init__(self, spec, False, module)\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.fields = []\n+    self.attributes = attributes\n+\n+  def Repr(self, as_ref=True):\n+    if as_ref:\n+      return '<%s spec=%r is_nullable=%r fields=%s>' % (\n+          self.__class__.__name__, self.spec, self.is_nullable, Repr(\n+              self.fields))\n+    else:\n+      return GenericRepr(self, {'fields': True, 'is_nullable': False})\n+\n+  def AddField(self, mojom_name, kind, ordinal=None, attributes=None):\n+    field = UnionField(mojom_name, kind, ordinal, None, attributes)\n+    self.fields.append(field)\n+    return field\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeUnion(self.mojom_name)\n+    for field in self.fields:\n+      field.Stylize(stylizer)\n+\n+  def IsBackwardCompatible(self, older_union):\n+    \"\"\"This union is backward-compatible with older_union if and only if all\n+    of the following conditions hold:\n+      - Any newly added field is tagged with a [MinVersion] attribute specifying\n+        a version number greater than all previously used [MinVersion]\n+        attributes within the union.\n+      - All fields present in older_union remain present in the new union,\n+        with the same ordinal value, same optional or non-optional status,\n+        same (or backward-compatible) type, and where applicable, the same\n+        [MinVersion] attribute value.\n+    \"\"\"\n+\n+    def buildOrdinalFieldMap(union):\n+      fields_by_ordinal = {}\n+      for field in union.fields:\n+        if field.ordinal in fields_by_ordinal:\n+          raise Exception('Multiple fields with ordinal %s in union %s.' %\n+                          (field.ordinal, union.mojom_name))\n+        fields_by_ordinal[field.ordinal] = field\n+      return fields_by_ordinal\n+\n+    new_fields = buildOrdinalFieldMap(self)\n+    old_fields = buildOrdinalFieldMap(older_union)\n+    if len(new_fields) < len(old_fields):\n+      # At least one field was removed, which is not OK.\n+      return False\n+\n+    max_old_min_version = 0\n+    for ordinal, old_field in old_fields.items():\n+      new_field = new_fields.get(ordinal)\n+      if not new_field:\n+        # A field was removed, which is not OK.\n+        return False\n+      if not _IsFieldBackwardCompatible(new_field, old_field):\n+        # An field changed its type or MinVersion, which is not OK.\n+        return False\n+      old_min_version = old_field.min_version or 0\n+      if old_min_version > max_old_min_version:\n+        max_old_min_version = old_min_version\n+\n+    new_ordinals = set(new_fields.keys()) - set(old_fields.keys())\n+    for ordinal in new_ordinals:\n+      if (new_fields[ordinal].min_version or 0) <= max_old_min_version:\n+        # New fields must use a MinVersion greater than any old fields.\n+        return False\n+\n+    return True\n+\n+  @property\n+  def stable(self):\n+    return self.attributes.get(ATTRIBUTE_STABLE, False) \\\n+        if self.attributes else False\n+\n+  @property\n+  def qualified_name(self):\n+    if self.parent_kind:\n+      prefix = self.parent_kind.qualified_name + '.'\n+    else:\n+      prefix = self.module.GetNamespacePrefix()\n+    return '%s%s' % (prefix, self.mojom_name)\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Union) and\n+            (self.mojom_name, self.fields,\n+             self.attributes) == (rhs.mojom_name, rhs.fields, rhs.attributes))\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class Array(ReferenceKind):\n+  \"\"\"An array.\n+\n+  Attributes:\n+    kind: {Kind} The type of the elements. May be None.\n+    length: The number of elements. None if unknown.\n+  \"\"\"\n+\n+  ReferenceKind.AddSharedProperty('kind')\n+  ReferenceKind.AddSharedProperty('length')\n+\n+  def __init__(self, kind=None, length=None):\n+    if kind is not None:\n+      if length is not None:\n+        spec = 'a%d:%s' % (length, kind.spec)\n+      else:\n+        spec = 'a:%s' % kind.spec\n+\n+      ReferenceKind.__init__(self, spec)\n+    else:\n+      ReferenceKind.__init__(self)\n+    self.kind = kind\n+    self.length = length\n+\n+  def Repr(self, as_ref=True):\n+    if as_ref:\n+      return '<%s spec=%r is_nullable=%r kind=%s length=%r>' % (\n+          self.__class__.__name__, self.spec, self.is_nullable, Repr(\n+              self.kind), self.length)\n+    else:\n+      return GenericRepr(self, {\n+          'kind': True,\n+          'length': False,\n+          'is_nullable': False\n+      })\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Array)\n+            and (self.kind, self.length) == (rhs.kind, rhs.length))\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class Map(ReferenceKind):\n+  \"\"\"A map.\n+\n+  Attributes:\n+    key_kind: {Kind} The type of the keys. May be None.\n+    value_kind: {Kind} The type of the elements. May be None.\n+  \"\"\"\n+  ReferenceKind.AddSharedProperty('key_kind')\n+  ReferenceKind.AddSharedProperty('value_kind')\n+\n+  def __init__(self, key_kind=None, value_kind=None):\n+    if (key_kind is not None and value_kind is not None):\n+      ReferenceKind.__init__(\n+          self, 'm[' + key_kind.spec + '][' + value_kind.spec + ']')\n+      if IsNullableKind(key_kind):\n+        raise Exception(\"Nullable kinds cannot be keys in maps.\")\n+      if IsAnyHandleKind(key_kind):\n+        raise Exception(\"Handles cannot be keys in maps.\")\n+      if IsAnyInterfaceKind(key_kind):\n+        raise Exception(\"Interfaces cannot be keys in maps.\")\n+      if IsArrayKind(key_kind):\n+        raise Exception(\"Arrays cannot be keys in maps.\")\n+    else:\n+      ReferenceKind.__init__(self)\n+\n+    self.key_kind = key_kind\n+    self.value_kind = value_kind\n+\n+  def Repr(self, as_ref=True):\n+    if as_ref:\n+      return '<%s spec=%r is_nullable=%r key_kind=%s value_kind=%s>' % (\n+          self.__class__.__name__, self.spec, self.is_nullable,\n+          Repr(self.key_kind), Repr(self.value_kind))\n+    else:\n+      return GenericRepr(self, {'key_kind': True, 'value_kind': True})\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Map) and\n+            (self.key_kind, self.value_kind) == (rhs.key_kind, rhs.value_kind))\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class PendingRemote(ReferenceKind):\n+  ReferenceKind.AddSharedProperty('kind')\n+\n+  def __init__(self, kind=None):\n+    if kind is not None:\n+      if not isinstance(kind, Interface):\n+        raise Exception(\n+            'pending_remote<T> requires T to be an interface type. Got %r' %\n+            kind.spec)\n+      ReferenceKind.__init__(self, 'rmt:' + kind.spec)\n+    else:\n+      ReferenceKind.__init__(self)\n+    self.kind = kind\n+\n+  def __eq__(self, rhs):\n+    return isinstance(rhs, PendingRemote) and self.kind == rhs.kind\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class PendingReceiver(ReferenceKind):\n+  ReferenceKind.AddSharedProperty('kind')\n+\n+  def __init__(self, kind=None):\n+    if kind is not None:\n+      if not isinstance(kind, Interface):\n+        raise Exception(\n+            'pending_receiver<T> requires T to be an interface type. Got %r' %\n+            kind.spec)\n+      ReferenceKind.__init__(self, 'rcv:' + kind.spec)\n+    else:\n+      ReferenceKind.__init__(self)\n+    self.kind = kind\n+\n+  def __eq__(self, rhs):\n+    return isinstance(rhs, PendingReceiver) and self.kind == rhs.kind\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class PendingAssociatedRemote(ReferenceKind):\n+  ReferenceKind.AddSharedProperty('kind')\n+\n+  def __init__(self, kind=None):\n+    if kind is not None:\n+      if not isinstance(kind, Interface):\n+        raise Exception(\n+            'pending_associated_remote<T> requires T to be an interface ' +\n+            'type. Got %r' % kind.spec)\n+      ReferenceKind.__init__(self, 'rma:' + kind.spec)\n+    else:\n+      ReferenceKind.__init__(self)\n+    self.kind = kind\n+\n+  def __eq__(self, rhs):\n+    return isinstance(rhs, PendingAssociatedRemote) and self.kind == rhs.kind\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class PendingAssociatedReceiver(ReferenceKind):\n+  ReferenceKind.AddSharedProperty('kind')\n+\n+  def __init__(self, kind=None):\n+    if kind is not None:\n+      if not isinstance(kind, Interface):\n+        raise Exception(\n+            'pending_associated_receiver<T> requires T to be an interface' +\n+            'type. Got %r' % kind.spec)\n+      ReferenceKind.__init__(self, 'rca:' + kind.spec)\n+    else:\n+      ReferenceKind.__init__(self)\n+    self.kind = kind\n+\n+  def __eq__(self, rhs):\n+    return isinstance(rhs, PendingAssociatedReceiver) and self.kind == rhs.kind\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class InterfaceRequest(ReferenceKind):\n+  ReferenceKind.AddSharedProperty('kind')\n+\n+  def __init__(self, kind=None):\n+    if kind is not None:\n+      if not isinstance(kind, Interface):\n+        raise Exception(\n+            \"Interface request requires %r to be an interface.\" % kind.spec)\n+      ReferenceKind.__init__(self, 'r:' + kind.spec)\n+    else:\n+      ReferenceKind.__init__(self)\n+    self.kind = kind\n+\n+  def __eq__(self, rhs):\n+    return isinstance(rhs, InterfaceRequest) and self.kind == rhs.kind\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class AssociatedInterfaceRequest(ReferenceKind):\n+  ReferenceKind.AddSharedProperty('kind')\n+\n+  def __init__(self, kind=None):\n+    if kind is not None:\n+      if not isinstance(kind, InterfaceRequest):\n+        raise Exception(\n+            \"Associated interface request requires %r to be an interface \"\n+            \"request.\" % kind.spec)\n+      assert not kind.is_nullable\n+      ReferenceKind.__init__(self, 'asso:' + kind.spec)\n+    else:\n+      ReferenceKind.__init__(self)\n+    self.kind = kind.kind if kind is not None else None\n+\n+  def __eq__(self, rhs):\n+    return isinstance(rhs, AssociatedInterfaceRequest) and self.kind == rhs.kind\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class Parameter(object):\n+  def __init__(self,\n+               mojom_name=None,\n+               kind=None,\n+               ordinal=None,\n+               default=None,\n+               attributes=None):\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.ordinal = ordinal\n+    self.kind = kind\n+    self.default = default\n+    self.attributes = attributes\n+\n+  def Repr(self, as_ref=True):\n+    # pylint: disable=unused-argument\n+    return '<%s mojom_name=%r kind=%s>' % (\n+        self.__class__.__name__, self.mojom_name, self.kind.Repr(as_ref=True))\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeParameter(self.mojom_name)\n+\n+  @property\n+  def min_version(self):\n+    return self.attributes.get(ATTRIBUTE_MIN_VERSION) \\\n+        if self.attributes else None\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Parameter)\n+            and (self.mojom_name, self.ordinal, self.kind, self.default,\n+                 self.attributes) == (rhs.mojom_name, rhs.ordinal, rhs.kind,\n+                                      rhs.default, rhs.attributes))\n+\n+\n+class Method(object):\n+  def __init__(self, interface, mojom_name, ordinal=None, attributes=None):\n+    self.interface = interface\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.explicit_ordinal = ordinal\n+    self.ordinal = ordinal\n+    self.parameters = []\n+    self.param_struct = None\n+    self.response_parameters = None\n+    self.response_param_struct = None\n+    self.attributes = attributes\n+\n+  def Repr(self, as_ref=True):\n+    if as_ref:\n+      return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)\n+    else:\n+      return GenericRepr(self, {\n+          'mojom_name': False,\n+          'parameters': True,\n+          'response_parameters': True\n+      })\n+\n+  def AddParameter(self,\n+                   mojom_name,\n+                   kind,\n+                   ordinal=None,\n+                   default=None,\n+                   attributes=None):\n+    parameter = Parameter(mojom_name, kind, ordinal, default, attributes)\n+    self.parameters.append(parameter)\n+    return parameter\n+\n+  def AddResponseParameter(self,\n+                           mojom_name,\n+                           kind,\n+                           ordinal=None,\n+                           default=None,\n+                           attributes=None):\n+    if self.response_parameters == None:\n+      self.response_parameters = []\n+    parameter = Parameter(mojom_name, kind, ordinal, default, attributes)\n+    self.response_parameters.append(parameter)\n+    return parameter\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeMethod(self.mojom_name)\n+    for param in self.parameters:\n+      param.Stylize(stylizer)\n+    if self.response_parameters is not None:\n+      for param in self.response_parameters:\n+        param.Stylize(stylizer)\n+\n+    if self.param_struct:\n+      self.param_struct.Stylize(stylizer)\n+    if self.response_param_struct:\n+      self.response_param_struct.Stylize(stylizer)\n+\n+  @property\n+  def min_version(self):\n+    return self.attributes.get(ATTRIBUTE_MIN_VERSION) \\\n+        if self.attributes else None\n+\n+  @property\n+  def sync(self):\n+    return self.attributes.get(ATTRIBUTE_SYNC) \\\n+        if self.attributes else None\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Method) and\n+            (self.mojom_name, self.ordinal, self.parameters,\n+             self.response_parameters,\n+             self.attributes) == (rhs.mojom_name, rhs.ordinal, rhs.parameters,\n+                                  rhs.response_parameters, rhs.attributes))\n+\n+\n+class Interface(ReferenceKind):\n+  ReferenceKind.AddSharedProperty('mojom_name')\n+  ReferenceKind.AddSharedProperty('name')\n+  ReferenceKind.AddSharedProperty('methods')\n+  ReferenceKind.AddSharedProperty('enums')\n+  ReferenceKind.AddSharedProperty('constants')\n+  ReferenceKind.AddSharedProperty('attributes')\n+\n+  def __init__(self, mojom_name=None, module=None, attributes=None):\n+    if mojom_name is not None:\n+      spec = 'x:' + mojom_name\n+    else:\n+      spec = None\n+    ReferenceKind.__init__(self, spec, False, module)\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.methods = []\n+    self.enums = []\n+    self.constants = []\n+    self.attributes = attributes\n+\n+  def Repr(self, as_ref=True):\n+    if as_ref:\n+      return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)\n+    else:\n+      return GenericRepr(self, {\n+          'mojom_name': False,\n+          'attributes': False,\n+          'methods': False\n+      })\n+\n+  def AddMethod(self, mojom_name, ordinal=None, attributes=None):\n+    method = Method(self, mojom_name, ordinal, attributes)\n+    self.methods.append(method)\n+    return method\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeInterface(self.mojom_name)\n+    for method in self.methods:\n+      method.Stylize(stylizer)\n+    for enum in self.enums:\n+      enum.Stylize(stylizer)\n+    for constant in self.constants:\n+      constant.Stylize(stylizer)\n+\n+  def IsBackwardCompatible(self, older_interface):\n+    \"\"\"This interface is backward-compatible with older_interface if and only\n+    if all of the following conditions hold:\n+      - All defined methods in older_interface (when identified by ordinal) have\n+        backward-compatible definitions in this interface. For each method this\n+        means:\n+          - The parameter list is backward-compatible, according to backward-\n+            compatibility rules for structs, where each parameter is essentially\n+            a struct field.\n+          - If the old method definition does not specify a reply message, the\n+            new method definition must not specify a reply message.\n+          - If the old method definition specifies a reply message, the new\n+            method definition must also specify a reply message with a parameter\n+            list that is backward-compatible according to backward-compatibility\n+            rules for structs.\n+      - All newly introduced methods in this interface have a [MinVersion]\n+        attribute specifying a version greater than any method in\n+        older_interface.\n+    \"\"\"\n+\n+    def buildOrdinalMethodMap(interface):\n+      methods_by_ordinal = {}\n+      for method in interface.methods:\n+        if method.ordinal in methods_by_ordinal:\n+          raise Exception('Multiple methods with ordinal %s in interface %s.' %\n+                          (method.ordinal, interface.mojom_name))\n+        methods_by_ordinal[method.ordinal] = method\n+      return methods_by_ordinal\n+\n+    new_methods = buildOrdinalMethodMap(self)\n+    old_methods = buildOrdinalMethodMap(older_interface)\n+    max_old_min_version = 0\n+    for ordinal, old_method in old_methods.items():\n+      new_method = new_methods.get(ordinal)\n+      if not new_method:\n+        # A method was removed, which is not OK.\n+        return False\n+\n+      if not new_method.param_struct.IsBackwardCompatible(\n+          old_method.param_struct):\n+        # The parameter list is not backward-compatible, which is not OK.\n+        return False\n+\n+      if old_method.response_param_struct is None:\n+        if new_method.response_param_struct is not None:\n+          # A reply was added to a message which didn't have one before, and\n+          # this is not OK.\n+          return False\n+      else:\n+        if new_method.response_param_struct is None:\n+          # A reply was removed from a message, which is not OK.\n+          return False\n+        if not new_method.response_param_struct.IsBackwardCompatible(\n+            old_method.response_param_struct):\n+          # The new message's reply is not backward-compatible with the old\n+          # message's reply, which is not OK.\n+          return False\n+\n+      if (old_method.min_version or 0) > max_old_min_version:\n+        max_old_min_version = old_method.min_version\n+\n+    # All the old methods are compatible with their new counterparts. Now verify\n+    # that newly added methods are properly versioned.\n+    new_ordinals = set(new_methods.keys()) - set(old_methods.keys())\n+    for ordinal in new_ordinals:\n+      new_method = new_methods[ordinal]\n+      if (new_method.min_version or 0) <= max_old_min_version:\n+        # A method was added to an existing version, which is not OK.\n+        return False\n+\n+    return True\n+\n+  @property\n+  def stable(self):\n+    return self.attributes.get(ATTRIBUTE_STABLE, False) \\\n+        if self.attributes else False\n+\n+  @property\n+  def qualified_name(self):\n+    if self.parent_kind:\n+      prefix = self.parent_kind.qualified_name + '.'\n+    else:\n+      prefix = self.module.GetNamespacePrefix()\n+    return '%s%s' % (prefix, self.mojom_name)\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Interface)\n+            and (self.mojom_name, self.methods, self.enums, self.constants,\n+                 self.attributes) == (rhs.mojom_name, rhs.methods, rhs.enums,\n+                                      rhs.constants, rhs.attributes))\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class AssociatedInterface(ReferenceKind):\n+  ReferenceKind.AddSharedProperty('kind')\n+\n+  def __init__(self, kind=None):\n+    if kind is not None:\n+      if not isinstance(kind, Interface):\n+        raise Exception(\n+            \"Associated interface requires %r to be an interface.\" % kind.spec)\n+      assert not kind.is_nullable\n+      ReferenceKind.__init__(self, 'asso:' + kind.spec)\n+    else:\n+      ReferenceKind.__init__(self)\n+    self.kind = kind\n+\n+  def __eq__(self, rhs):\n+    return isinstance(rhs, AssociatedInterface) and self.kind == rhs.kind\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class EnumField(object):\n+  def __init__(self,\n+               mojom_name=None,\n+               value=None,\n+               attributes=None,\n+               numeric_value=None):\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.value = value\n+    self.attributes = attributes\n+    self.numeric_value = numeric_value\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeEnumField(self.mojom_name)\n+\n+  @property\n+  def min_version(self):\n+    return self.attributes.get(ATTRIBUTE_MIN_VERSION) \\\n+        if self.attributes else None\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, EnumField)\n+            and (self.mojom_name, self.value, self.attributes,\n+                 self.numeric_value) == (rhs.mojom_name, rhs.value,\n+                                         rhs.attributes, rhs.numeric_value))\n+\n+\n+class Enum(Kind):\n+  def __init__(self, mojom_name=None, module=None, attributes=None):\n+    self.mojom_name = mojom_name\n+    self.name = None\n+    self.native_only = False\n+    if mojom_name is not None:\n+      spec = 'x:' + mojom_name\n+    else:\n+      spec = None\n+    Kind.__init__(self, spec, module)\n+    self.fields = []\n+    self.attributes = attributes\n+    self.min_value = None\n+    self.max_value = None\n+\n+  def Repr(self, as_ref=True):\n+    if as_ref:\n+      return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)\n+    else:\n+      return GenericRepr(self, {'mojom_name': False, 'fields': False})\n+\n+  def Stylize(self, stylizer):\n+    self.name = stylizer.StylizeEnum(self.mojom_name)\n+    for field in self.fields:\n+      field.Stylize(stylizer)\n+\n+  @property\n+  def extensible(self):\n+    return self.attributes.get(ATTRIBUTE_EXTENSIBLE, False) \\\n+        if self.attributes else False\n+\n+  @property\n+  def stable(self):\n+    return self.attributes.get(ATTRIBUTE_STABLE, False) \\\n+        if self.attributes else False\n+\n+  @property\n+  def qualified_name(self):\n+    if self.parent_kind:\n+      prefix = self.parent_kind.qualified_name + '.'\n+    else:\n+      prefix = self.module.GetNamespacePrefix()\n+    return '%s%s' % (prefix, self.mojom_name)\n+\n+  def IsBackwardCompatible(self, older_enum):\n+    \"\"\"This enum is backward-compatible with older_enum if and only if one of\n+    the following conditions holds:\n+        - Neither enum is [Extensible] and both have the exact same set of valid\n+          numeric values. Field names and aliases for the same numeric value do\n+          not affect compatibility.\n+        - older_enum is [Extensible], and for every version defined by\n+          older_enum, this enum has the exact same set of valid numeric values.\n+    \"\"\"\n+\n+    def buildVersionFieldMap(enum):\n+      fields_by_min_version = {}\n+      for field in enum.fields:\n+        if field.min_version not in fields_by_min_version:\n+          fields_by_min_version[field.min_version] = set()\n+        fields_by_min_version[field.min_version].add(field.numeric_value)\n+      return fields_by_min_version\n+\n+    old_fields = buildVersionFieldMap(older_enum)\n+    new_fields = buildVersionFieldMap(self)\n+\n+    if new_fields.keys() != old_fields.keys() and not older_enum.extensible:\n+      return False\n+\n+    for min_version, valid_values in old_fields.items():\n+      if (min_version not in new_fields\n+          or new_fields[min_version] != valid_values):\n+        return False\n+\n+    return True\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Enum) and\n+            (self.mojom_name, self.native_only, self.fields, self.attributes,\n+             self.min_value,\n+             self.max_value) == (rhs.mojom_name, rhs.native_only, rhs.fields,\n+                                 rhs.attributes, rhs.min_value, rhs.max_value))\n+\n+  def __hash__(self):\n+    return id(self)\n+\n+\n+class Module(object):\n+  def __init__(self, path=None, mojom_namespace=None, attributes=None):\n+    self.path = path\n+    self.mojom_namespace = mojom_namespace\n+    self.namespace = None\n+    self.structs = []\n+    self.unions = []\n+    self.interfaces = []\n+    self.enums = []\n+    self.constants = []\n+    self.kinds = {}\n+    self.attributes = attributes\n+    self.imports = []\n+    self.imported_kinds = {}\n+\n+  def __repr__(self):\n+    # Gives us a decent __repr__ for modules.\n+    return self.Repr()\n+\n+  def __eq__(self, rhs):\n+    return (isinstance(rhs, Module) and\n+            (self.path, self.attributes, self.mojom_namespace, self.imports,\n+             self.constants, self.enums, self.structs, self.unions,\n+             self.interfaces) == (rhs.path, rhs.attributes, rhs.mojom_namespace,\n+                                  rhs.imports, rhs.constants, rhs.enums,\n+                                  rhs.structs, rhs.unions, rhs.interfaces))\n+\n+  def Repr(self, as_ref=True):\n+    if as_ref:\n+      return '<%s path=%r mojom_namespace=%r>' % (\n+          self.__class__.__name__, self.path, self.mojom_namespace)\n+    else:\n+      return GenericRepr(\n+          self, {\n+              'path': False,\n+              'mojom_namespace': False,\n+              'attributes': False,\n+              'structs': False,\n+              'interfaces': False,\n+              'unions': False\n+          })\n+\n+  def GetNamespacePrefix(self):\n+    return '%s.' % self.mojom_namespace if self.mojom_namespace else ''\n+\n+  def AddInterface(self, mojom_name, attributes=None):\n+    interface = Interface(mojom_name, self, attributes)\n+    self.interfaces.append(interface)\n+    return interface\n+\n+  def AddStruct(self, mojom_name, attributes=None):\n+    struct = Struct(mojom_name, self, attributes)\n+    self.structs.append(struct)\n+    return struct\n+\n+  def AddUnion(self, mojom_name, attributes=None):\n+    union = Union(mojom_name, self, attributes)\n+    self.unions.append(union)\n+    return union\n+\n+  def Stylize(self, stylizer):\n+    self.namespace = stylizer.StylizeModule(self.mojom_namespace)\n+    for struct in self.structs:\n+      struct.Stylize(stylizer)\n+    for union in self.unions:\n+      union.Stylize(stylizer)\n+    for interface in self.interfaces:\n+      interface.Stylize(stylizer)\n+    for enum in self.enums:\n+      enum.Stylize(stylizer)\n+    for constant in self.constants:\n+      constant.Stylize(stylizer)\n+\n+    for imported_module in self.imports:\n+      imported_module.Stylize(stylizer)\n+\n+  def Dump(self, f):\n+    pickle.dump(self, f, 2)\n+\n+  @classmethod\n+  def Load(cls, f):\n+    result = pickle.load(f)\n+    assert isinstance(result, Module)\n+    return result\n+\n+\n+def IsBoolKind(kind):\n+  return kind.spec == BOOL.spec\n+\n+\n+def IsFloatKind(kind):\n+  return kind.spec == FLOAT.spec\n+\n+\n+def IsDoubleKind(kind):\n+  return kind.spec == DOUBLE.spec\n+\n+\n+def IsIntegralKind(kind):\n+  return (kind.spec == BOOL.spec or kind.spec == INT8.spec\n+          or kind.spec == INT16.spec or kind.spec == INT32.spec\n+          or kind.spec == INT64.spec or kind.spec == UINT8.spec\n+          or kind.spec == UINT16.spec or kind.spec == UINT32.spec\n+          or kind.spec == UINT64.spec)\n+\n+\n+def IsStringKind(kind):\n+  return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec\n+\n+\n+def IsGenericHandleKind(kind):\n+  return kind.spec == HANDLE.spec or kind.spec == NULLABLE_HANDLE.spec\n+\n+\n+def IsDataPipeConsumerKind(kind):\n+  return kind.spec == DCPIPE.spec or kind.spec == NULLABLE_DCPIPE.spec\n+\n+\n+def IsDataPipeProducerKind(kind):\n+  return kind.spec == DPPIPE.spec or kind.spec == NULLABLE_DPPIPE.spec\n+\n+\n+def IsMessagePipeKind(kind):\n+  return kind.spec == MSGPIPE.spec or kind.spec == NULLABLE_MSGPIPE.spec\n+\n+\n+def IsSharedBufferKind(kind):\n+  return (kind.spec == SHAREDBUFFER.spec\n+          or kind.spec == NULLABLE_SHAREDBUFFER.spec)\n+\n+\n+def IsPlatformHandleKind(kind):\n+  return (kind.spec == PLATFORMHANDLE.spec\n+          or kind.spec == NULLABLE_PLATFORMHANDLE.spec)\n+\n+\n+def IsStructKind(kind):\n+  return isinstance(kind, Struct)\n+\n+\n+def IsUnionKind(kind):\n+  return isinstance(kind, Union)\n+\n+\n+def IsArrayKind(kind):\n+  return isinstance(kind, Array)\n+\n+\n+def IsInterfaceKind(kind):\n+  return isinstance(kind, Interface)\n+\n+\n+def IsAssociatedInterfaceKind(kind):\n+  return isinstance(kind, AssociatedInterface)\n+\n+\n+def IsInterfaceRequestKind(kind):\n+  return isinstance(kind, InterfaceRequest)\n+\n+\n+def IsAssociatedInterfaceRequestKind(kind):\n+  return isinstance(kind, AssociatedInterfaceRequest)\n+\n+\n+def IsPendingRemoteKind(kind):\n+  return isinstance(kind, PendingRemote)\n+\n+\n+def IsPendingReceiverKind(kind):\n+  return isinstance(kind, PendingReceiver)\n+\n+\n+def IsPendingAssociatedRemoteKind(kind):\n+  return isinstance(kind, PendingAssociatedRemote)\n+\n+\n+def IsPendingAssociatedReceiverKind(kind):\n+  return isinstance(kind, PendingAssociatedReceiver)\n+\n+\n+def IsEnumKind(kind):\n+  return isinstance(kind, Enum)\n+\n+\n+def IsReferenceKind(kind):\n+  return isinstance(kind, ReferenceKind)\n+\n+\n+def IsNullableKind(kind):\n+  return IsReferenceKind(kind) and kind.is_nullable\n+\n+\n+def IsMapKind(kind):\n+  return isinstance(kind, Map)\n+\n+\n+def IsObjectKind(kind):\n+  return IsPointerKind(kind) or IsUnionKind(kind)\n+\n+\n+def IsPointerKind(kind):\n+  return (IsStructKind(kind) or IsArrayKind(kind) or IsStringKind(kind)\n+          or IsMapKind(kind))\n+\n+\n+# Please note that it doesn't include any interface kind.\n+def IsAnyHandleKind(kind):\n+  return (IsGenericHandleKind(kind) or IsDataPipeConsumerKind(kind)\n+          or IsDataPipeProducerKind(kind) or IsMessagePipeKind(kind)\n+          or IsSharedBufferKind(kind) or IsPlatformHandleKind(kind))\n+\n+\n+def IsAnyInterfaceKind(kind):\n+  return (IsInterfaceKind(kind) or IsInterfaceRequestKind(kind)\n+          or IsAssociatedKind(kind) or IsPendingRemoteKind(kind)\n+          or IsPendingReceiverKind(kind))\n+\n+\n+def IsAnyHandleOrInterfaceKind(kind):\n+  return IsAnyHandleKind(kind) or IsAnyInterfaceKind(kind)\n+\n+\n+def IsAssociatedKind(kind):\n+  return (IsAssociatedInterfaceKind(kind)\n+          or IsAssociatedInterfaceRequestKind(kind)\n+          or IsPendingAssociatedRemoteKind(kind)\n+          or IsPendingAssociatedReceiverKind(kind))\n+\n+\n+def HasCallbacks(interface):\n+  for method in interface.methods:\n+    if method.response_parameters != None:\n+      return True\n+  return False\n+\n+\n+# Finds out whether an interface passes associated interfaces and associated\n+# interface requests.\n+def PassesAssociatedKinds(interface):\n+  visited_kinds = set()\n+  for method in interface.methods:\n+    if MethodPassesAssociatedKinds(method, visited_kinds):\n+      return True\n+  return False\n+\n+\n+def _AnyMethodParameterRecursive(method, predicate, visited_kinds=None):\n+  def _HasProperty(kind):\n+    if kind in visited_kinds:\n+      # No need to examine the kind again.\n+      return False\n+    visited_kinds.add(kind)\n+    if predicate(kind):\n+      return True\n+    if IsArrayKind(kind):\n+      return _HasProperty(kind.kind)\n+    if IsStructKind(kind) or IsUnionKind(kind):\n+      for field in kind.fields:\n+        if _HasProperty(field.kind):\n+          return True\n+    if IsMapKind(kind):\n+      if _HasProperty(kind.key_kind) or _HasProperty(kind.value_kind):\n+        return True\n+    return False\n+\n+  if visited_kinds is None:\n+    visited_kinds = set()\n+\n+  for param in method.parameters:\n+    if _HasProperty(param.kind):\n+      return True\n+  if method.response_parameters != None:\n+    for param in method.response_parameters:\n+      if _HasProperty(param.kind):\n+        return True\n+  return False\n+\n+\n+# Finds out whether a method passes associated interfaces and associated\n+# interface requests.\n+def MethodPassesAssociatedKinds(method, visited_kinds=None):\n+  return _AnyMethodParameterRecursive(\n+      method, IsAssociatedKind, visited_kinds=visited_kinds)\n+\n+\n+# Determines whether a method passes interfaces.\n+def MethodPassesInterfaces(method):\n+  return _AnyMethodParameterRecursive(method, IsInterfaceKind)\n+\n+\n+def HasSyncMethods(interface):\n+  for method in interface.methods:\n+    if method.sync:\n+      return True\n+  return False\n+\n+\n+def ContainsHandlesOrInterfaces(kind):\n+  \"\"\"Check if the kind contains any handles.\n+\n+  This check is recursive so it checks all struct fields, containers elements,\n+  etc.\n+\n+  Args:\n+    struct: {Kind} The kind to check.\n+\n+  Returns:\n+    {bool}: True if the kind contains handles.\n+  \"\"\"\n+  # We remember the types we already checked to avoid infinite recursion when\n+  # checking recursive (or mutually recursive) types:\n+  checked = set()\n+\n+  def Check(kind):\n+    if kind.spec in checked:\n+      return False\n+    checked.add(kind.spec)\n+    if IsStructKind(kind):\n+      return any(Check(field.kind) for field in kind.fields)\n+    elif IsUnionKind(kind):\n+      return any(Check(field.kind) for field in kind.fields)\n+    elif IsAnyHandleKind(kind):\n+      return True\n+    elif IsAnyInterfaceKind(kind):\n+      return True\n+    elif IsArrayKind(kind):\n+      return Check(kind.kind)\n+    elif IsMapKind(kind):\n+      return Check(kind.key_kind) or Check(kind.value_kind)\n+    else:\n+      return False\n+\n+  return Check(kind)\n+\n+\n+def ContainsNativeTypes(kind):\n+  \"\"\"Check if the kind contains any native type (struct or enum).\n+\n+  This check is recursive so it checks all struct fields, scoped interface\n+  enums, etc.\n+\n+  Args:\n+    struct: {Kind} The kind to check.\n+\n+  Returns:\n+    {bool}: True if the kind contains native types.\n+  \"\"\"\n+  # We remember the types we already checked to avoid infinite recursion when\n+  # checking recursive (or mutually recursive) types:\n+  checked = set()\n+\n+  def Check(kind):\n+    if kind.spec in checked:\n+      return False\n+    checked.add(kind.spec)\n+    if IsEnumKind(kind):\n+      return kind.native_only\n+    elif IsStructKind(kind):\n+      if kind.native_only:\n+        return True\n+      if any(enum.native_only for enum in kind.enums):\n+        return True\n+      return any(Check(field.kind) for field in kind.fields)\n+    elif IsUnionKind(kind):\n+      return any(Check(field.kind) for field in kind.fields)\n+    elif IsInterfaceKind(kind):\n+      return any(enum.native_only for enum in kind.enums)\n+    elif IsArrayKind(kind):\n+      return Check(kind.kind)\n+    elif IsMapKind(kind):\n+      return Check(kind.key_kind) or Check(kind.value_kind)\n+    else:\n+      return False\n+\n+  return Check(kind)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py\nnew file mode 100644\nindex 00000000..e8fd4936\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py\n@@ -0,0 +1,31 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import sys\n+import unittest\n+\n+from mojom.generate import module as mojom\n+\n+\n+class ModuleTest(unittest.TestCase):\n+  def testNonInterfaceAsInterfaceRequest(self):\n+    \"\"\"Tests that a non-interface cannot be used for interface requests.\"\"\"\n+    module = mojom.Module('test_module', 'test_namespace')\n+    struct = mojom.Struct('TestStruct', module=module)\n+    with self.assertRaises(Exception) as e:\n+      mojom.InterfaceRequest(struct)\n+    self.assertEquals(\n+        e.exception.__str__(),\n+        'Interface request requires \\'x:TestStruct\\' to be an interface.')\n+\n+  def testNonInterfaceAsAssociatedInterface(self):\n+    \"\"\"Tests that a non-interface type cannot be used for associated interfaces.\n+    \"\"\"\n+    module = mojom.Module('test_module', 'test_namespace')\n+    struct = mojom.Struct('TestStruct', module=module)\n+    with self.assertRaises(Exception) as e:\n+      mojom.AssociatedInterface(struct)\n+    self.assertEquals(\n+        e.exception.__str__(),\n+        'Associated interface requires \\'x:TestStruct\\' to be an interface.')\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py\nnew file mode 100644\nindex 00000000..88b77c98\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py\n@@ -0,0 +1,258 @@\n+# Copyright 2013 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+from mojom.generate import module as mojom\n+\n+# This module provides a mechanism for determining the packed order and offsets\n+# of a mojom.Struct.\n+#\n+# ps = pack.PackedStruct(struct)\n+# ps.packed_fields will access a list of PackedField objects, each of which\n+# will have an offset, a size and a bit (for mojom.BOOLs).\n+\n+# Size of struct header in bytes: num_bytes [4B] + version [4B].\n+HEADER_SIZE = 8\n+\n+\n+class PackedField(object):\n+  kind_to_size = {\n+      mojom.BOOL: 1,\n+      mojom.INT8: 1,\n+      mojom.UINT8: 1,\n+      mojom.INT16: 2,\n+      mojom.UINT16: 2,\n+      mojom.INT32: 4,\n+      mojom.UINT32: 4,\n+      mojom.FLOAT: 4,\n+      mojom.HANDLE: 4,\n+      mojom.MSGPIPE: 4,\n+      mojom.SHAREDBUFFER: 4,\n+      mojom.PLATFORMHANDLE: 4,\n+      mojom.DCPIPE: 4,\n+      mojom.DPPIPE: 4,\n+      mojom.NULLABLE_HANDLE: 4,\n+      mojom.NULLABLE_MSGPIPE: 4,\n+      mojom.NULLABLE_SHAREDBUFFER: 4,\n+      mojom.NULLABLE_PLATFORMHANDLE: 4,\n+      mojom.NULLABLE_DCPIPE: 4,\n+      mojom.NULLABLE_DPPIPE: 4,\n+      mojom.INT64: 8,\n+      mojom.UINT64: 8,\n+      mojom.DOUBLE: 8,\n+      mojom.STRING: 8,\n+      mojom.NULLABLE_STRING: 8\n+  }\n+\n+  @classmethod\n+  def GetSizeForKind(cls, kind):\n+    if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct, mojom.Interface,\n+                         mojom.AssociatedInterface, mojom.PendingRemote,\n+                         mojom.PendingAssociatedRemote)):\n+      return 8\n+    if isinstance(kind, mojom.Union):\n+      return 16\n+    if isinstance(kind, (mojom.InterfaceRequest, mojom.PendingReceiver)):\n+      kind = mojom.MSGPIPE\n+    if isinstance(\n+        kind,\n+        (mojom.AssociatedInterfaceRequest, mojom.PendingAssociatedReceiver)):\n+      return 4\n+    if isinstance(kind, mojom.Enum):\n+      # TODO(mpcomplete): what about big enums?\n+      return cls.kind_to_size[mojom.INT32]\n+    if not kind in cls.kind_to_size:\n+      raise Exception(\"Undefined type: %s. Did you forget to import the file \"\n+                      \"containing the definition?\" % kind.spec)\n+    return cls.kind_to_size[kind]\n+\n+  @classmethod\n+  def GetAlignmentForKind(cls, kind):\n+    if isinstance(kind, (mojom.Interface, mojom.AssociatedInterface,\n+                         mojom.PendingRemote, mojom.PendingAssociatedRemote)):\n+      return 4\n+    if isinstance(kind, mojom.Union):\n+      return 8\n+    return cls.GetSizeForKind(kind)\n+\n+  def __init__(self, field, index, ordinal):\n+    \"\"\"\n+    Args:\n+      field: the original field.\n+      index: the position of the original field in the struct.\n+      ordinal: the ordinal of the field for serialization.\n+    \"\"\"\n+    self.field = field\n+    self.index = index\n+    self.ordinal = ordinal\n+    self.size = self.GetSizeForKind(field.kind)\n+    self.alignment = self.GetAlignmentForKind(field.kind)\n+    self.offset = None\n+    self.bit = None\n+    self.min_version = None\n+\n+\n+def GetPad(offset, alignment):\n+  \"\"\"Returns the pad necessary to reserve space so that |offset + pad| equals to\n+  some multiple of |alignment|.\"\"\"\n+  return (alignment - (offset % alignment)) % alignment\n+\n+\n+def GetFieldOffset(field, last_field):\n+  \"\"\"Returns a 2-tuple of the field offset and bit (for BOOLs).\"\"\"\n+  if (field.field.kind == mojom.BOOL and last_field.field.kind == mojom.BOOL\n+      and last_field.bit < 7):\n+    return (last_field.offset, last_field.bit + 1)\n+\n+  offset = last_field.offset + last_field.size\n+  pad = GetPad(offset, field.alignment)\n+  return (offset + pad, 0)\n+\n+\n+def GetPayloadSizeUpToField(field):\n+  \"\"\"Returns the payload size (not including struct header) if |field| is the\n+  last field.\n+  \"\"\"\n+  if not field:\n+    return 0\n+  offset = field.offset + field.size\n+  pad = GetPad(offset, 8)\n+  return offset + pad\n+\n+\n+class PackedStruct(object):\n+  def __init__(self, struct):\n+    self.struct = struct\n+    # |packed_fields| contains all the fields, in increasing offset order.\n+    self.packed_fields = []\n+    # |packed_fields_in_ordinal_order| refers to the same fields as\n+    # |packed_fields|, but in ordinal order.\n+    self.packed_fields_in_ordinal_order = []\n+\n+    # No fields.\n+    if (len(struct.fields) == 0):\n+      return\n+\n+    # Start by sorting by ordinal.\n+    src_fields = self.packed_fields_in_ordinal_order\n+    ordinal = 0\n+    for index, field in enumerate(struct.fields):\n+      if field.ordinal is not None:\n+        ordinal = field.ordinal\n+      src_fields.append(PackedField(field, index, ordinal))\n+      ordinal += 1\n+    src_fields.sort(key=lambda field: field.ordinal)\n+\n+    # Set |min_version| for each field.\n+    next_min_version = 0\n+    for packed_field in src_fields:\n+      if packed_field.field.min_version is None:\n+        assert next_min_version == 0\n+      else:\n+        assert packed_field.field.min_version >= next_min_version\n+        next_min_version = packed_field.field.min_version\n+      packed_field.min_version = next_min_version\n+\n+      if (packed_field.min_version != 0\n+          and mojom.IsReferenceKind(packed_field.field.kind)\n+          and not packed_field.field.kind.is_nullable):\n+        raise Exception(\"Non-nullable fields are only allowed in version 0 of \"\n+                        \"a struct. %s.%s is defined with [MinVersion=%d].\" %\n+                        (self.struct.name, packed_field.field.name,\n+                         packed_field.min_version))\n+\n+    src_field = src_fields[0]\n+    src_field.offset = 0\n+    src_field.bit = 0\n+    dst_fields = self.packed_fields\n+    dst_fields.append(src_field)\n+\n+    # Then find first slot that each field will fit.\n+    for src_field in src_fields[1:]:\n+      last_field = dst_fields[0]\n+      for i in range(1, len(dst_fields)):\n+        next_field = dst_fields[i]\n+        offset, bit = GetFieldOffset(src_field, last_field)\n+        if offset + src_field.size <= next_field.offset:\n+          # Found hole.\n+          src_field.offset = offset\n+          src_field.bit = bit\n+          dst_fields.insert(i, src_field)\n+          break\n+        last_field = next_field\n+      if src_field.offset is None:\n+        # Add to end\n+        src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)\n+        dst_fields.append(src_field)\n+\n+\n+class ByteInfo(object):\n+  def __init__(self):\n+    self.is_padding = False\n+    self.packed_fields = []\n+\n+\n+def GetByteLayout(packed_struct):\n+  total_payload_size = GetPayloadSizeUpToField(\n+      packed_struct.packed_fields[-1] if packed_struct.packed_fields else None)\n+  byte_info = [ByteInfo() for i in range(total_payload_size)]\n+\n+  limit_of_previous_field = 0\n+  for packed_field in packed_struct.packed_fields:\n+    for i in range(limit_of_previous_field, packed_field.offset):\n+      byte_info[i].is_padding = True\n+    byte_info[packed_field.offset].packed_fields.append(packed_field)\n+    limit_of_previous_field = packed_field.offset + packed_field.size\n+\n+  for i in range(limit_of_previous_field, len(byte_info)):\n+    byte_info[i].is_padding = True\n+\n+  for byte in byte_info:\n+    # A given byte cannot both be padding and have a fields packed into it.\n+    assert not (byte.is_padding and byte.packed_fields)\n+\n+  return byte_info\n+\n+\n+class VersionInfo(object):\n+  def __init__(self, version, num_fields, num_bytes):\n+    self.version = version\n+    self.num_fields = num_fields\n+    self.num_bytes = num_bytes\n+\n+\n+def GetVersionInfo(packed_struct):\n+  \"\"\"Get version information for a struct.\n+\n+  Args:\n+    packed_struct: A PackedStruct instance.\n+\n+  Returns:\n+    A non-empty list of VersionInfo instances, sorted by version in increasing\n+    order.\n+    Note: The version numbers may not be consecutive.\n+  \"\"\"\n+  versions = []\n+  last_version = 0\n+  last_num_fields = 0\n+  last_payload_size = 0\n+\n+  for packed_field in packed_struct.packed_fields_in_ordinal_order:\n+    if packed_field.min_version != last_version:\n+      versions.append(\n+          VersionInfo(last_version, last_num_fields,\n+                      last_payload_size + HEADER_SIZE))\n+      last_version = packed_field.min_version\n+\n+    last_num_fields += 1\n+    # The fields are iterated in ordinal order here. However, the size of a\n+    # version is determined by the last field of that version in pack order,\n+    # instead of ordinal order. Therefore, we need to calculate the max value.\n+    last_payload_size = max(\n+        GetPayloadSizeUpToField(packed_field), last_payload_size)\n+\n+  assert len(versions) == 0 or last_num_fields != versions[-1].num_fields\n+  versions.append(\n+      VersionInfo(last_version, last_num_fields,\n+                  last_payload_size + HEADER_SIZE))\n+  return versions\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py\nnew file mode 100644\nindex 00000000..98c705ad\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py\n@@ -0,0 +1,225 @@\n+# Copyright 2013 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import sys\n+import unittest\n+\n+from mojom.generate import module as mojom\n+from mojom.generate import pack\n+\n+\n+class PackTest(unittest.TestCase):\n+  def testOrdinalOrder(self):\n+    struct = mojom.Struct('test')\n+    struct.AddField('testfield1', mojom.INT32, 2)\n+    struct.AddField('testfield2', mojom.INT32, 1)\n+    ps = pack.PackedStruct(struct)\n+\n+    self.assertEqual(2, len(ps.packed_fields))\n+    self.assertEqual('testfield2', ps.packed_fields[0].field.mojom_name)\n+    self.assertEqual('testfield1', ps.packed_fields[1].field.mojom_name)\n+\n+  def testZeroFields(self):\n+    struct = mojom.Struct('test')\n+    ps = pack.PackedStruct(struct)\n+    self.assertEqual(0, len(ps.packed_fields))\n+\n+  def testOneField(self):\n+    struct = mojom.Struct('test')\n+    struct.AddField('testfield1', mojom.INT8)\n+    ps = pack.PackedStruct(struct)\n+    self.assertEqual(1, len(ps.packed_fields))\n+\n+  def _CheckPackSequence(self, kinds, fields, offsets):\n+    \"\"\"Checks the pack order and offsets of a sequence of mojom.Kinds.\n+\n+    Args:\n+      kinds: A sequence of mojom.Kinds that specify the fields that are to be\n+      created.\n+      fields: The expected order of the resulting fields, with the integer \"1\"\n+      first.\n+      offsets: The expected order of offsets, with the integer \"0\" first.\n+    \"\"\"\n+    struct = mojom.Struct('test')\n+    index = 1\n+    for kind in kinds:\n+      struct.AddField('%d' % index, kind)\n+      index += 1\n+    ps = pack.PackedStruct(struct)\n+    num_fields = len(ps.packed_fields)\n+    self.assertEqual(len(kinds), num_fields)\n+    for i in range(num_fields):\n+      self.assertEqual('%d' % fields[i], ps.packed_fields[i].field.mojom_name)\n+      self.assertEqual(offsets[i], ps.packed_fields[i].offset)\n+\n+  def testPaddingPackedInOrder(self):\n+    return self._CheckPackSequence((mojom.INT8, mojom.UINT8, mojom.INT32),\n+                                   (1, 2, 3), (0, 1, 4))\n+\n+  def testPaddingPackedOutOfOrder(self):\n+    return self._CheckPackSequence((mojom.INT8, mojom.INT32, mojom.UINT8),\n+                                   (1, 3, 2), (0, 1, 4))\n+\n+  def testPaddingPackedOverflow(self):\n+    kinds = (mojom.INT8, mojom.INT32, mojom.INT16, mojom.INT8, mojom.INT8)\n+    # 2 bytes should be packed together first, followed by short, then by int.\n+    fields = (1, 4, 3, 2, 5)\n+    offsets = (0, 1, 2, 4, 8)\n+    return self._CheckPackSequence(kinds, fields, offsets)\n+\n+  def testNullableTypes(self):\n+    kinds = (mojom.STRING.MakeNullableKind(), mojom.HANDLE.MakeNullableKind(),\n+             mojom.Struct('test_struct').MakeNullableKind(),\n+             mojom.DCPIPE.MakeNullableKind(), mojom.Array().MakeNullableKind(),\n+             mojom.DPPIPE.MakeNullableKind(),\n+             mojom.Array(length=5).MakeNullableKind(),\n+             mojom.MSGPIPE.MakeNullableKind(),\n+             mojom.Interface('test_interface').MakeNullableKind(),\n+             mojom.SHAREDBUFFER.MakeNullableKind(),\n+             mojom.InterfaceRequest().MakeNullableKind())\n+    fields = (1, 2, 4, 3, 5, 6, 8, 7, 9, 10, 11)\n+    offsets = (0, 8, 12, 16, 24, 32, 36, 40, 48, 56, 60)\n+    return self._CheckPackSequence(kinds, fields, offsets)\n+\n+  def testAllTypes(self):\n+    return self._CheckPackSequence(\n+        (mojom.BOOL, mojom.INT8, mojom.STRING, mojom.UINT8, mojom.INT16,\n+         mojom.DOUBLE, mojom.UINT16, mojom.INT32, mojom.UINT32, mojom.INT64,\n+         mojom.FLOAT, mojom.STRING, mojom.HANDLE, mojom.UINT64,\n+         mojom.Struct('test'), mojom.Array(), mojom.STRING.MakeNullableKind()),\n+        (1, 2, 4, 5, 7, 3, 6, 8, 9, 10, 11, 13, 12, 14, 15, 16, 17, 18),\n+        (0, 1, 2, 4, 6, 8, 16, 24, 28, 32, 40, 44, 48, 56, 64, 72, 80, 88))\n+\n+  def testPaddingPackedOutOfOrderByOrdinal(self):\n+    struct = mojom.Struct('test')\n+    struct.AddField('testfield1', mojom.INT8)\n+    struct.AddField('testfield3', mojom.UINT8, 3)\n+    struct.AddField('testfield2', mojom.INT32, 2)\n+    ps = pack.PackedStruct(struct)\n+    self.assertEqual(3, len(ps.packed_fields))\n+\n+    # Second byte should be packed in behind first, altering order.\n+    self.assertEqual('testfield1', ps.packed_fields[0].field.mojom_name)\n+    self.assertEqual('testfield3', ps.packed_fields[1].field.mojom_name)\n+    self.assertEqual('testfield2', ps.packed_fields[2].field.mojom_name)\n+\n+    # Second byte should be packed with first.\n+    self.assertEqual(0, ps.packed_fields[0].offset)\n+    self.assertEqual(1, ps.packed_fields[1].offset)\n+    self.assertEqual(4, ps.packed_fields[2].offset)\n+\n+  def testBools(self):\n+    struct = mojom.Struct('test')\n+    struct.AddField('bit0', mojom.BOOL)\n+    struct.AddField('bit1', mojom.BOOL)\n+    struct.AddField('int', mojom.INT32)\n+    struct.AddField('bit2', mojom.BOOL)\n+    struct.AddField('bit3', mojom.BOOL)\n+    struct.AddField('bit4', mojom.BOOL)\n+    struct.AddField('bit5', mojom.BOOL)\n+    struct.AddField('bit6', mojom.BOOL)\n+    struct.AddField('bit7', mojom.BOOL)\n+    struct.AddField('bit8', mojom.BOOL)\n+    ps = pack.PackedStruct(struct)\n+    self.assertEqual(10, len(ps.packed_fields))\n+\n+    # First 8 bits packed together.\n+    for i in range(8):\n+      pf = ps.packed_fields[i]\n+      self.assertEqual(0, pf.offset)\n+      self.assertEqual(\"bit%d\" % i, pf.field.mojom_name)\n+      self.assertEqual(i, pf.bit)\n+\n+    # Ninth bit goes into second byte.\n+    self.assertEqual(\"bit8\", ps.packed_fields[8].field.mojom_name)\n+    self.assertEqual(1, ps.packed_fields[8].offset)\n+    self.assertEqual(0, ps.packed_fields[8].bit)\n+\n+    # int comes last.\n+    self.assertEqual(\"int\", ps.packed_fields[9].field.mojom_name)\n+    self.assertEqual(4, ps.packed_fields[9].offset)\n+\n+  def testMinVersion(self):\n+    \"\"\"Tests that |min_version| is properly set for packed fields.\"\"\"\n+    struct = mojom.Struct('test')\n+    struct.AddField('field_2', mojom.BOOL, 2)\n+    struct.AddField('field_0', mojom.INT32, 0)\n+    struct.AddField('field_1', mojom.INT64, 1)\n+    ps = pack.PackedStruct(struct)\n+\n+    self.assertEqual('field_0', ps.packed_fields[0].field.mojom_name)\n+    self.assertEqual('field_2', ps.packed_fields[1].field.mojom_name)\n+    self.assertEqual('field_1', ps.packed_fields[2].field.mojom_name)\n+\n+    self.assertEqual(0, ps.packed_fields[0].min_version)\n+    self.assertEqual(0, ps.packed_fields[1].min_version)\n+    self.assertEqual(0, ps.packed_fields[2].min_version)\n+\n+    struct.fields[0].attributes = {'MinVersion': 1}\n+    ps = pack.PackedStruct(struct)\n+\n+    self.assertEqual(0, ps.packed_fields[0].min_version)\n+    self.assertEqual(1, ps.packed_fields[1].min_version)\n+    self.assertEqual(0, ps.packed_fields[2].min_version)\n+\n+  def testGetVersionInfoEmptyStruct(self):\n+    \"\"\"Tests that pack.GetVersionInfo() never returns an empty list, even for\n+    empty structs.\n+    \"\"\"\n+    struct = mojom.Struct('test')\n+    ps = pack.PackedStruct(struct)\n+\n+    versions = pack.GetVersionInfo(ps)\n+    self.assertEqual(1, len(versions))\n+    self.assertEqual(0, versions[0].version)\n+    self.assertEqual(0, versions[0].num_fields)\n+    self.assertEqual(8, versions[0].num_bytes)\n+\n+  def testGetVersionInfoComplexOrder(self):\n+    \"\"\"Tests pack.GetVersionInfo() using a struct whose definition order,\n+    ordinal order and pack order for fields are all different.\n+    \"\"\"\n+    struct = mojom.Struct('test')\n+    struct.AddField(\n+        'field_3', mojom.BOOL, ordinal=3, attributes={'MinVersion': 3})\n+    struct.AddField('field_0', mojom.INT32, ordinal=0)\n+    struct.AddField(\n+        'field_1', mojom.INT64, ordinal=1, attributes={'MinVersion': 2})\n+    struct.AddField(\n+        'field_2', mojom.INT64, ordinal=2, attributes={'MinVersion': 3})\n+    ps = pack.PackedStruct(struct)\n+\n+    versions = pack.GetVersionInfo(ps)\n+    self.assertEqual(3, len(versions))\n+\n+    self.assertEqual(0, versions[0].version)\n+    self.assertEqual(1, versions[0].num_fields)\n+    self.assertEqual(16, versions[0].num_bytes)\n+\n+    self.assertEqual(2, versions[1].version)\n+    self.assertEqual(2, versions[1].num_fields)\n+    self.assertEqual(24, versions[1].num_bytes)\n+\n+    self.assertEqual(3, versions[2].version)\n+    self.assertEqual(4, versions[2].num_fields)\n+    self.assertEqual(32, versions[2].num_bytes)\n+\n+  def testInterfaceAlignment(self):\n+    \"\"\"Tests that interfaces are aligned on 4-byte boundaries, although the size\n+    of an interface is 8 bytes.\n+    \"\"\"\n+    kinds = (mojom.INT32, mojom.Interface('test_interface'))\n+    fields = (1, 2)\n+    offsets = (0, 4)\n+    self._CheckPackSequence(kinds, fields, offsets)\n+\n+  def testAssociatedInterfaceAlignment(self):\n+    \"\"\"Tests that associated interfaces are aligned on 4-byte boundaries,\n+    although the size of an associated interface is 8 bytes.\n+    \"\"\"\n+    kinds = (mojom.INT32,\n+             mojom.AssociatedInterface(mojom.Interface('test_interface')))\n+    fields = (1, 2)\n+    offsets = (0, 4)\n+    self._CheckPackSequence(kinds, fields, offsets)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py\nnew file mode 100644\nindex 00000000..7a300560\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py\n@@ -0,0 +1,83 @@\n+# Copyright 2013 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+# Based on third_party/WebKit/Source/build/scripts/template_expander.py.\n+\n+import os.path\n+import sys\n+\n+from mojom import fileutil\n+\n+fileutil.AddLocalRepoThirdPartyDirToModulePath()\n+import jinja2\n+\n+\n+def ApplyTemplate(mojo_generator, path_to_template, params, **kwargs):\n+  loader = jinja2.ModuleLoader(\n+      os.path.join(mojo_generator.bytecode_path,\n+                   \"%s.zip\" % mojo_generator.GetTemplatePrefix()))\n+  final_kwargs = dict(mojo_generator.GetJinjaParameters())\n+  final_kwargs.update(kwargs)\n+\n+  jinja_env = jinja2.Environment(\n+      loader=loader, keep_trailing_newline=True, **final_kwargs)\n+  jinja_env.globals.update(mojo_generator.GetGlobals())\n+  jinja_env.filters.update(mojo_generator.GetFilters())\n+  template = jinja_env.get_template(path_to_template)\n+  return template.render(params)\n+\n+\n+def UseJinja(path_to_template, **kwargs):\n+  def RealDecorator(generator):\n+    def GeneratorInternal(*args, **kwargs2):\n+      parameters = generator(*args, **kwargs2)\n+      return ApplyTemplate(args[0], path_to_template, parameters, **kwargs)\n+\n+    GeneratorInternal.__name__ = generator.__name__\n+    return GeneratorInternal\n+\n+  return RealDecorator\n+\n+\n+def ApplyImportedTemplate(mojo_generator, path_to_template, filename, params,\n+                          **kwargs):\n+  loader = jinja2.FileSystemLoader(searchpath=path_to_template)\n+  final_kwargs = dict(mojo_generator.GetJinjaParameters())\n+  final_kwargs.update(kwargs)\n+\n+  jinja_env = jinja2.Environment(\n+      loader=loader, keep_trailing_newline=True, **final_kwargs)\n+  jinja_env.globals.update(mojo_generator.GetGlobals())\n+  jinja_env.filters.update(mojo_generator.GetFilters())\n+  template = jinja_env.get_template(filename)\n+  return template.render(params)\n+\n+\n+def UseJinjaForImportedTemplate(func):\n+  def wrapper(*args, **kwargs):\n+    parameters = func(*args, **kwargs)\n+    path_to_template = args[1]\n+    filename = args[2]\n+    return ApplyImportedTemplate(args[0], path_to_template, filename,\n+                                 parameters)\n+\n+  wrapper.__name__ = func.__name__\n+  return wrapper\n+\n+\n+def PrecompileTemplates(generator_modules, output_dir):\n+  for module in generator_modules.values():\n+    generator = module.Generator(None)\n+    jinja_env = jinja2.Environment(\n+        loader=jinja2.FileSystemLoader([\n+            os.path.join(\n+                os.path.dirname(module.__file__), generator.GetTemplatePrefix())\n+        ]))\n+    jinja_env.filters.update(generator.GetFilters())\n+    jinja_env.compile_templates(\n+        os.path.join(output_dir, \"%s.zip\" % generator.GetTemplatePrefix()),\n+        extensions=[\"tmpl\"],\n+        zip=\"stored\",\n+        py_compile=True,\n+        ignore_errors=False)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py\nnew file mode 100644\nindex 00000000..d6df3ca6\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py\n@@ -0,0 +1,854 @@\n+# Copyright 2013 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Convert parse tree to AST.\n+\n+This module converts the parse tree to the AST we use for code generation. The\n+main entry point is OrderedModule, which gets passed the parser\n+representation of a mojom file. When called it's assumed that all imports have\n+already been parsed and converted to ASTs before.\n+\"\"\"\n+\n+import itertools\n+import os\n+import re\n+import sys\n+\n+from mojom.generate import generator\n+from mojom.generate import module as mojom\n+from mojom.parse import ast\n+\n+\n+def _IsStrOrUnicode(x):\n+  if sys.version_info[0] < 3:\n+    return isinstance(x, (unicode, str))\n+  return isinstance(x, str)\n+\n+\n+def _DuplicateName(values):\n+  \"\"\"Returns the 'mojom_name' of the first entry in |values| whose 'mojom_name'\n+  has already been encountered. If there are no duplicates, returns None.\"\"\"\n+  names = set()\n+  for value in values:\n+    if value.mojom_name in names:\n+      return value.mojom_name\n+    names.add(value.mojom_name)\n+  return None\n+\n+\n+def _ElemsOfType(elems, elem_type, scope):\n+  \"\"\"Find all elements of the given type.\n+\n+  Args:\n+    elems: {Sequence[Any]} Sequence of elems.\n+    elem_type: {Type[C]} Extract all elems of this type.\n+    scope: {str} The name of the surrounding scope (e.g. struct\n+        definition). Used in error messages.\n+\n+  Returns:\n+    {List[C]} All elems of matching type.\n+  \"\"\"\n+  assert isinstance(elem_type, type)\n+  result = [elem for elem in elems if isinstance(elem, elem_type)]\n+  duplicate_name = _DuplicateName(result)\n+  if duplicate_name:\n+    raise Exception('Names in mojom must be unique within a scope. The name '\n+                    '\"%s\" is used more than once within the scope \"%s\".' %\n+                    (duplicate_name, scope))\n+  return result\n+\n+\n+def _ProcessElements(scope, elements, operations_by_type):\n+  \"\"\"Iterates over the given elements, running a function from\n+  operations_by_type for any element that matches a key in that dict. The scope\n+  is the name of the surrounding scope, such as a filename or struct name, used\n+  only in error messages.\"\"\"\n+  names_in_this_scope = set()\n+  for element in elements:\n+    # pylint: disable=unidiomatic-typecheck\n+    element_type = type(element)\n+    if element_type in operations_by_type:\n+      if element.mojom_name in names_in_this_scope:\n+        raise Exception('Names must be unique within a scope. The name \"%s\" is '\n+                        'used more than once within the scope \"%s\".' %\n+                        (duplicate_name, scope))\n+      operations_by_type[element_type](element)\n+\n+\n+def _MapKind(kind):\n+  map_to_kind = {\n+      'bool': 'b',\n+      'int8': 'i8',\n+      'int16': 'i16',\n+      'int32': 'i32',\n+      'int64': 'i64',\n+      'uint8': 'u8',\n+      'uint16': 'u16',\n+      'uint32': 'u32',\n+      'uint64': 'u64',\n+      'float': 'f',\n+      'double': 'd',\n+      'string': 's',\n+      'handle': 'h',\n+      'handle<data_pipe_consumer>': 'h:d:c',\n+      'handle<data_pipe_producer>': 'h:d:p',\n+      'handle<message_pipe>': 'h:m',\n+      'handle<shared_buffer>': 'h:s',\n+      'handle<platform>': 'h:p'\n+  }\n+  if kind.endswith('?'):\n+    base_kind = _MapKind(kind[0:-1])\n+    # NOTE: This doesn't rule out enum types. Those will be detected later, when\n+    # cross-reference is established.\n+    reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso', 'rmt', 'rcv',\n+                       'rma', 'rca')\n+    if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:\n+      raise Exception('A type (spec \"%s\") cannot be made nullable' % base_kind)\n+    return '?' + base_kind\n+  if kind.endswith('}'):\n+    lbracket = kind.rfind('{')\n+    value = kind[0:lbracket]\n+    return 'm[' + _MapKind(kind[lbracket + 1:-1]) + '][' + _MapKind(value) + ']'\n+  if kind.endswith(']'):\n+    lbracket = kind.rfind('[')\n+    typename = kind[0:lbracket]\n+    return 'a' + kind[lbracket + 1:-1] + ':' + _MapKind(typename)\n+  if kind.endswith('&'):\n+    return 'r:' + _MapKind(kind[0:-1])\n+  if kind.startswith('asso<'):\n+    assert kind.endswith('>')\n+    return 'asso:' + _MapKind(kind[5:-1])\n+  if kind.startswith('rmt<'):\n+    assert kind.endswith('>')\n+    return 'rmt:' + _MapKind(kind[4:-1])\n+  if kind.startswith('rcv<'):\n+    assert kind.endswith('>')\n+    return 'rcv:' + _MapKind(kind[4:-1])\n+  if kind.startswith('rma<'):\n+    assert kind.endswith('>')\n+    return 'rma:' + _MapKind(kind[4:-1])\n+  if kind.startswith('rca<'):\n+    assert kind.endswith('>')\n+    return 'rca:' + _MapKind(kind[4:-1])\n+  if kind in map_to_kind:\n+    return map_to_kind[kind]\n+  return 'x:' + kind\n+\n+\n+def _AttributeListToDict(attribute_list):\n+  if attribute_list is None:\n+    return None\n+  assert isinstance(attribute_list, ast.AttributeList)\n+  # TODO(vtl): Check for duplicate keys here.\n+  return dict(\n+      [(attribute.key, attribute.value) for attribute in attribute_list])\n+\n+\n+builtin_values = frozenset([\n+    \"double.INFINITY\", \"double.NEGATIVE_INFINITY\", \"double.NAN\",\n+    \"float.INFINITY\", \"float.NEGATIVE_INFINITY\", \"float.NAN\"\n+])\n+\n+\n+def _IsBuiltinValue(value):\n+  return value in builtin_values\n+\n+\n+def _LookupKind(kinds, spec, scope):\n+  \"\"\"Tries to find which Kind a spec refers to, given the scope in which its\n+  referenced. Starts checking from the narrowest scope to most general. For\n+  example, given a struct field like\n+    Foo.Bar x;\n+  Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner\n+  type 'Bar' in the struct 'Foo' in the current namespace.\n+\n+  |scope| is a tuple that looks like (namespace, struct/interface), referring\n+  to the location where the type is referenced.\"\"\"\n+  if spec.startswith('x:'):\n+    mojom_name = spec[2:]\n+    for i in range(len(scope), -1, -1):\n+      test_spec = 'x:'\n+      if i > 0:\n+        test_spec += '.'.join(scope[:i]) + '.'\n+      test_spec += mojom_name\n+      kind = kinds.get(test_spec)\n+      if kind:\n+        return kind\n+\n+  return kinds.get(spec)\n+\n+\n+def _GetScopeForKind(module, kind):\n+  \"\"\"For a given kind, returns a tuple of progressively more specific names\n+  used to qualify the kind. For example if kind is an enum named Bar nested in a\n+  struct Foo within module 'foo', this would return ('foo', 'Foo', 'Bar')\"\"\"\n+  if isinstance(kind, mojom.Enum) and kind.parent_kind:\n+    # Enums may be nested in other kinds.\n+    return _GetScopeForKind(module, kind.parent_kind) + (kind.mojom_name, )\n+\n+  module_fragment = (module.mojom_namespace, ) if module.mojom_namespace else ()\n+  kind_fragment = (kind.mojom_name, ) if kind else ()\n+  return module_fragment + kind_fragment\n+\n+\n+def _LookupValueInScope(module, kind, identifier):\n+  \"\"\"Given a kind and an identifier, this attempts to resolve the given\n+  identifier to a concrete NamedValue within the scope of the given kind.\"\"\"\n+  scope = _GetScopeForKind(module, kind)\n+  for i in reversed(range(len(scope) + 1)):\n+    qualified_name = '.'.join(scope[:i] + (identifier, ))\n+    value = module.values.get(qualified_name)\n+    if value:\n+      return value\n+  return None\n+\n+\n+def _LookupValue(module, parent_kind, implied_kind, ast_leaf_node):\n+  \"\"\"Resolves a leaf node in the form ('IDENTIFIER', 'x') to a constant value\n+  identified by 'x' in some mojom definition. parent_kind is used as context\n+  when resolving the identifier. If the given leaf node is not an IDENTIFIER\n+  (e.g. already a constant value), it is returned as-is.\n+\n+  If implied_kind is provided, the parsed identifier may also be resolved within\n+  its scope as fallback. This can be useful for more concise value references\n+  when assigning enum-typed constants or field values.\"\"\"\n+  if not isinstance(ast_leaf_node, tuple) or ast_leaf_node[0] != 'IDENTIFIER':\n+    return ast_leaf_node\n+\n+  # First look for a known user-defined identifier to resolve this within the\n+  # enclosing scope.\n+  identifier = ast_leaf_node[1]\n+\n+  value = _LookupValueInScope(module, parent_kind, identifier)\n+  if value:\n+    return value\n+\n+  # Next look in the scope of implied_kind, if provided.\n+  value = (implied_kind and implied_kind.module and _LookupValueInScope(\n+      implied_kind.module, implied_kind, identifier))\n+  if value:\n+    return value\n+\n+  # Fall back on defined builtin symbols\n+  if _IsBuiltinValue(identifier):\n+    return mojom.BuiltinValue(identifier)\n+\n+  raise ValueError('Unknown identifier %s' % identifier)\n+\n+\n+def _Kind(kinds, spec, scope):\n+  \"\"\"Convert a type name into a mojom.Kind object.\n+\n+  As a side-effect this function adds the result to 'kinds'.\n+\n+  Args:\n+    kinds: {Dict[str, mojom.Kind]} All known kinds up to this point, indexed by\n+        their names.\n+    spec: {str} A name uniquely identifying a type.\n+    scope: {Tuple[str, str]} A tuple that looks like (namespace,\n+        struct/interface), referring to the location where the type is\n+        referenced.\n+\n+  Returns:\n+    {mojom.Kind} The type corresponding to 'spec'.\n+  \"\"\"\n+  kind = _LookupKind(kinds, spec, scope)\n+  if kind:\n+    return kind\n+\n+  if spec.startswith('?'):\n+    kind = _Kind(kinds, spec[1:], scope).MakeNullableKind()\n+  elif spec.startswith('a:'):\n+    kind = mojom.Array(_Kind(kinds, spec[2:], scope))\n+  elif spec.startswith('asso:'):\n+    inner_kind = _Kind(kinds, spec[5:], scope)\n+    if isinstance(inner_kind, mojom.InterfaceRequest):\n+      kind = mojom.AssociatedInterfaceRequest(inner_kind)\n+    else:\n+      kind = mojom.AssociatedInterface(inner_kind)\n+  elif spec.startswith('a'):\n+    colon = spec.find(':')\n+    length = int(spec[1:colon])\n+    kind = mojom.Array(_Kind(kinds, spec[colon + 1:], scope), length)\n+  elif spec.startswith('r:'):\n+    kind = mojom.InterfaceRequest(_Kind(kinds, spec[2:], scope))\n+  elif spec.startswith('rmt:'):\n+    kind = mojom.PendingRemote(_Kind(kinds, spec[4:], scope))\n+  elif spec.startswith('rcv:'):\n+    kind = mojom.PendingReceiver(_Kind(kinds, spec[4:], scope))\n+  elif spec.startswith('rma:'):\n+    kind = mojom.PendingAssociatedRemote(_Kind(kinds, spec[4:], scope))\n+  elif spec.startswith('rca:'):\n+    kind = mojom.PendingAssociatedReceiver(_Kind(kinds, spec[4:], scope))\n+  elif spec.startswith('m['):\n+    # Isolate the two types from their brackets.\n+\n+    # It is not allowed to use map as key, so there shouldn't be nested ']'s\n+    # inside the key type spec.\n+    key_end = spec.find(']')\n+    assert key_end != -1 and key_end < len(spec) - 1\n+    assert spec[key_end + 1] == '[' and spec[-1] == ']'\n+\n+    first_kind = spec[2:key_end]\n+    second_kind = spec[key_end + 2:-1]\n+\n+    kind = mojom.Map(\n+        _Kind(kinds, first_kind, scope), _Kind(kinds, second_kind, scope))\n+  else:\n+    kind = mojom.Kind(spec)\n+\n+  kinds[spec] = kind\n+  return kind\n+\n+\n+def _Import(module, import_module):\n+  # Copy the struct kinds from our imports into the current module.\n+  importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface)\n+  for kind in import_module.kinds.values():\n+    if (isinstance(kind, importable_kinds)\n+        and kind.module.path == import_module.path):\n+      module.kinds[kind.spec] = kind\n+  # Ditto for values.\n+  for value in import_module.values.values():\n+    if value.module.path == import_module.path:\n+      module.values[value.GetSpec()] = value\n+\n+  return import_module\n+\n+\n+def _Struct(module, parsed_struct):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_struct: {ast.Struct} Parsed struct.\n+\n+  Returns:\n+    {mojom.Struct} AST struct.\n+  \"\"\"\n+  struct = mojom.Struct(module=module)\n+  struct.mojom_name = parsed_struct.mojom_name\n+  struct.native_only = parsed_struct.body is None\n+  struct.spec = 'x:' + module.GetNamespacePrefix() + struct.mojom_name\n+  module.kinds[struct.spec] = struct\n+  struct.enums = []\n+  struct.constants = []\n+  struct.fields_data = []\n+  if not struct.native_only:\n+    _ProcessElements(\n+        parsed_struct.mojom_name, parsed_struct.body, {\n+            ast.Enum:\n+            lambda enum: struct.enums.append(_Enum(module, enum, struct)),\n+            ast.Const:\n+            lambda const: struct.constants.append(\n+                _Constant(module, const, struct)),\n+            ast.StructField:\n+            struct.fields_data.append,\n+        })\n+\n+  struct.attributes = _AttributeListToDict(parsed_struct.attribute_list)\n+\n+  # Enforce that a [Native] attribute is set to make native-only struct\n+  # declarations more explicit.\n+  if struct.native_only:\n+    if not struct.attributes or not struct.attributes.get('Native', False):\n+      raise Exception(\"Native-only struct declarations must include a \" +\n+                      \"Native attribute.\")\n+\n+  if struct.attributes and struct.attributes.get('CustomSerializer', False):\n+    struct.custom_serializer = True\n+\n+  return struct\n+\n+\n+def _Union(module, parsed_union):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_union: {ast.Union} Parsed union.\n+\n+  Returns:\n+    {mojom.Union} AST union.\n+  \"\"\"\n+  union = mojom.Union(module=module)\n+  union.mojom_name = parsed_union.mojom_name\n+  union.spec = 'x:' + module.GetNamespacePrefix() + union.mojom_name\n+  module.kinds[union.spec] = union\n+  # Stash fields parsed_union here temporarily.\n+  union.fields_data = []\n+  _ProcessElements(parsed_union.mojom_name, parsed_union.body,\n+                   {ast.UnionField: union.fields_data.append})\n+  union.attributes = _AttributeListToDict(parsed_union.attribute_list)\n+  return union\n+\n+\n+def _StructField(module, parsed_field, struct):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_field: {ast.StructField} Parsed struct field.\n+    struct: {mojom.Struct} Struct this field belongs to.\n+\n+  Returns:\n+    {mojom.StructField} AST struct field.\n+  \"\"\"\n+  field = mojom.StructField()\n+  field.mojom_name = parsed_field.mojom_name\n+  field.kind = _Kind(module.kinds, _MapKind(parsed_field.typename),\n+                     (module.mojom_namespace, struct.mojom_name))\n+  field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None\n+  field.default = _LookupValue(module, struct, field.kind,\n+                               parsed_field.default_value)\n+  field.attributes = _AttributeListToDict(parsed_field.attribute_list)\n+  return field\n+\n+\n+def _UnionField(module, parsed_field, union):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_field: {ast.UnionField} Parsed union field.\n+    union: {mojom.Union} Union this fields belong to.\n+\n+  Returns:\n+    {mojom.UnionField} AST union.\n+  \"\"\"\n+  field = mojom.UnionField()\n+  field.mojom_name = parsed_field.mojom_name\n+  field.kind = _Kind(module.kinds, _MapKind(parsed_field.typename),\n+                     (module.mojom_namespace, union.mojom_name))\n+  field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None\n+  field.default = None\n+  field.attributes = _AttributeListToDict(parsed_field.attribute_list)\n+  return field\n+\n+\n+def _Parameter(module, parsed_param, interface):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_param: {ast.Parameter} Parsed parameter.\n+    union: {mojom.Interface} Interface this parameter belongs to.\n+\n+  Returns:\n+    {mojom.Parameter} AST parameter.\n+  \"\"\"\n+  parameter = mojom.Parameter()\n+  parameter.mojom_name = parsed_param.mojom_name\n+  parameter.kind = _Kind(module.kinds, _MapKind(parsed_param.typename),\n+                         (module.mojom_namespace, interface.mojom_name))\n+  parameter.ordinal = (parsed_param.ordinal.value\n+                       if parsed_param.ordinal else None)\n+  parameter.default = None  # TODO(tibell): We never have these. Remove field?\n+  parameter.attributes = _AttributeListToDict(parsed_param.attribute_list)\n+  return parameter\n+\n+\n+def _Method(module, parsed_method, interface):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_method: {ast.Method} Parsed method.\n+    interface: {mojom.Interface} Interface this method belongs to.\n+\n+  Returns:\n+    {mojom.Method} AST method.\n+  \"\"\"\n+  method = mojom.Method(\n+      interface,\n+      parsed_method.mojom_name,\n+      ordinal=parsed_method.ordinal.value if parsed_method.ordinal else None)\n+  method.parameters = list(\n+      map(lambda parameter: _Parameter(module, parameter, interface),\n+          parsed_method.parameter_list))\n+  if parsed_method.response_parameter_list is not None:\n+    method.response_parameters = list(\n+        map(lambda parameter: _Parameter(module, parameter, interface),\n+            parsed_method.response_parameter_list))\n+  method.attributes = _AttributeListToDict(parsed_method.attribute_list)\n+\n+  # Enforce that only methods with response can have a [Sync] attribute.\n+  if method.sync and method.response_parameters is None:\n+    raise Exception(\"Only methods with response can include a [Sync] \"\n+                    \"attribute. If no response parameters are needed, you \"\n+                    \"could use an empty response parameter list, i.e., \"\n+                    \"\\\"=> ()\\\".\")\n+\n+  return method\n+\n+\n+def _Interface(module, parsed_iface):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_iface: {ast.Interface} Parsed interface.\n+\n+  Returns:\n+    {mojom.Interface} AST interface.\n+  \"\"\"\n+  interface = mojom.Interface(module=module)\n+  interface.mojom_name = parsed_iface.mojom_name\n+  interface.spec = 'x:' + module.GetNamespacePrefix() + interface.mojom_name\n+  module.kinds[interface.spec] = interface\n+  interface.attributes = _AttributeListToDict(parsed_iface.attribute_list)\n+  interface.enums = []\n+  interface.constants = []\n+  interface.methods_data = []\n+  _ProcessElements(\n+      parsed_iface.mojom_name, parsed_iface.body, {\n+          ast.Enum:\n+          lambda enum: interface.enums.append(_Enum(module, enum, interface)),\n+          ast.Const:\n+          lambda const: interface.constants.append(\n+              _Constant(module, const, interface)),\n+          ast.Method:\n+          interface.methods_data.append,\n+      })\n+  return interface\n+\n+\n+def _EnumField(module, enum, parsed_field):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    enum: {mojom.Enum} Enum this field belongs to.\n+    parsed_field: {ast.EnumValue} Parsed enum value.\n+\n+  Returns:\n+    {mojom.EnumField} AST enum field.\n+  \"\"\"\n+  field = mojom.EnumField()\n+  field.mojom_name = parsed_field.mojom_name\n+  field.value = _LookupValue(module, enum, None, parsed_field.value)\n+  field.attributes = _AttributeListToDict(parsed_field.attribute_list)\n+  value = mojom.EnumValue(module, enum, field)\n+  module.values[value.GetSpec()] = value\n+  return field\n+\n+\n+def _ResolveNumericEnumValues(enum):\n+  \"\"\"\n+  Given a reference to a mojom.Enum, resolves and assigns the numeric value of\n+  each field, and also computes the min_value and max_value of the enum.\n+  \"\"\"\n+\n+  # map of <mojom_name> -> integral value\n+  prev_value = -1\n+  min_value = None\n+  max_value = None\n+  for field in enum.fields:\n+    # This enum value is +1 the previous enum value (e.g: BEGIN).\n+    if field.value is None:\n+      prev_value += 1\n+\n+    # Integral value (e.g: BEGIN = -0x1).\n+    elif _IsStrOrUnicode(field.value):\n+      prev_value = int(field.value, 0)\n+\n+    # Reference to a previous enum value (e.g: INIT = BEGIN).\n+    elif isinstance(field.value, mojom.EnumValue):\n+      prev_value = field.value.field.numeric_value\n+    elif isinstance(field.value, mojom.ConstantValue):\n+      constant = field.value.constant\n+      kind = constant.kind\n+      if not mojom.IsIntegralKind(kind) or mojom.IsBoolKind(kind):\n+        raise ValueError('Enum values must be integers. %s is not an integer.' %\n+                         constant.mojom_name)\n+      prev_value = int(constant.value, 0)\n+    else:\n+      raise Exception('Unresolved enum value for %s' % field.value.GetSpec())\n+\n+    #resolved_enum_values[field.mojom_name] = prev_value\n+    field.numeric_value = prev_value\n+    if min_value is None or prev_value < min_value:\n+      min_value = prev_value\n+    if max_value is None or prev_value > max_value:\n+      max_value = prev_value\n+\n+  enum.min_value = min_value\n+  enum.max_value = max_value\n+\n+\n+def _Enum(module, parsed_enum, parent_kind):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_enum: {ast.Enum} Parsed enum.\n+\n+  Returns:\n+    {mojom.Enum} AST enum.\n+  \"\"\"\n+  enum = mojom.Enum(module=module)\n+  enum.mojom_name = parsed_enum.mojom_name\n+  enum.native_only = parsed_enum.enum_value_list is None\n+  mojom_name = enum.mojom_name\n+  if parent_kind:\n+    mojom_name = parent_kind.mojom_name + '.' + mojom_name\n+  enum.spec = 'x:%s.%s' % (module.mojom_namespace, mojom_name)\n+  enum.parent_kind = parent_kind\n+  enum.attributes = _AttributeListToDict(parsed_enum.attribute_list)\n+\n+  if not enum.native_only:\n+    enum.fields = list(\n+        map(lambda field: _EnumField(module, enum, field),\n+            parsed_enum.enum_value_list))\n+    _ResolveNumericEnumValues(enum)\n+\n+  module.kinds[enum.spec] = enum\n+\n+  # Enforce that a [Native] attribute is set to make native-only enum\n+  # declarations more explicit.\n+  if enum.native_only:\n+    if not enum.attributes or not enum.attributes.get('Native', False):\n+      raise Exception(\"Native-only enum declarations must include a \" +\n+                      \"Native attribute.\")\n+\n+  return enum\n+\n+\n+def _Constant(module, parsed_const, parent_kind):\n+  \"\"\"\n+  Args:\n+    module: {mojom.Module} Module currently being constructed.\n+    parsed_const: {ast.Const} Parsed constant.\n+\n+  Returns:\n+    {mojom.Constant} AST constant.\n+  \"\"\"\n+  constant = mojom.Constant()\n+  constant.mojom_name = parsed_const.mojom_name\n+  if parent_kind:\n+    scope = (module.mojom_namespace, parent_kind.mojom_name)\n+  else:\n+    scope = (module.mojom_namespace, )\n+  # TODO(mpcomplete): maybe we should only support POD kinds.\n+  constant.kind = _Kind(module.kinds, _MapKind(parsed_const.typename), scope)\n+  constant.parent_kind = parent_kind\n+  constant.value = _LookupValue(module, parent_kind, constant.kind,\n+                                parsed_const.value)\n+\n+  # Iteratively resolve this constant reference to a concrete value\n+  while isinstance(constant.value, mojom.ConstantValue):\n+    constant.value = constant.value.constant.value\n+\n+  value = mojom.ConstantValue(module, parent_kind, constant)\n+  module.values[value.GetSpec()] = value\n+  return constant\n+\n+\n+def _CollectReferencedKinds(module, all_defined_kinds):\n+  \"\"\"\n+  Takes a {mojom.Module} object and a list of all defined kinds within that\n+  module, and enumerates the complete dict of user-defined mojom types\n+  (as {mojom.Kind} objects) referenced by the module's own defined kinds (i.e.\n+  as types of struct or union or interface parameters. The returned dict is\n+  keyed by kind spec.\n+  \"\"\"\n+\n+  def extract_referenced_user_kinds(kind):\n+    if mojom.IsArrayKind(kind):\n+      return extract_referenced_user_kinds(kind.kind)\n+    if mojom.IsMapKind(kind):\n+      return (extract_referenced_user_kinds(kind.key_kind) +\n+              extract_referenced_user_kinds(kind.value_kind))\n+    if mojom.IsInterfaceRequestKind(kind) or mojom.IsAssociatedKind(kind):\n+      return [kind.kind]\n+    if mojom.IsStructKind(kind):\n+      return [kind]\n+    if (mojom.IsInterfaceKind(kind) or mojom.IsEnumKind(kind)\n+        or mojom.IsUnionKind(kind)):\n+      return [kind]\n+    return []\n+\n+  def sanitize_kind(kind):\n+    \"\"\"Removes nullability from a kind\"\"\"\n+    if kind.spec.startswith('?'):\n+      return _Kind(module.kinds, kind.spec[1:], (module.mojom_namespace, ''))\n+    return kind\n+\n+  referenced_user_kinds = {}\n+  for defined_kind in all_defined_kinds:\n+    if mojom.IsStructKind(defined_kind) or mojom.IsUnionKind(defined_kind):\n+      for field in defined_kind.fields:\n+        for referenced_kind in extract_referenced_user_kinds(field.kind):\n+          sanitized_kind = sanitize_kind(referenced_kind)\n+          referenced_user_kinds[sanitized_kind.spec] = sanitized_kind\n+\n+  # Also scan for references in parameter lists\n+  for interface in module.interfaces:\n+    for method in interface.methods:\n+      for param in itertools.chain(method.parameters or [],\n+                                   method.response_parameters or []):\n+        if (mojom.IsStructKind(param.kind) or mojom.IsUnionKind(param.kind)\n+            or mojom.IsEnumKind(param.kind)\n+            or mojom.IsAnyInterfaceKind(param.kind)):\n+          for referenced_kind in extract_referenced_user_kinds(param.kind):\n+            sanitized_kind = sanitize_kind(referenced_kind)\n+            referenced_user_kinds[sanitized_kind.spec] = sanitized_kind\n+\n+  return referenced_user_kinds\n+\n+\n+def _AssignDefaultOrdinals(items):\n+  \"\"\"Assigns default ordinal values to a sequence of items if necessary.\"\"\"\n+  next_ordinal = 0\n+  for item in items:\n+    if item.ordinal is not None:\n+      next_ordinal = item.ordinal + 1\n+    else:\n+      item.ordinal = next_ordinal\n+      next_ordinal += 1\n+\n+\n+def _AssertTypeIsStable(kind):\n+  \"\"\"Raises an error if a type is not stable, meaning it is composed of at least\n+  one type that is not marked [Stable].\"\"\"\n+\n+  def assertDependencyIsStable(dependency):\n+    if (mojom.IsEnumKind(dependency) or mojom.IsStructKind(dependency)\n+        or mojom.IsUnionKind(dependency) or mojom.IsInterfaceKind(dependency)):\n+      if not dependency.stable:\n+        raise Exception(\n+            '%s is marked [Stable] but cannot be stable because it depends on '\n+            '%s, which is not marked [Stable].' %\n+            (kind.mojom_name, dependency.mojom_name))\n+    elif mojom.IsArrayKind(dependency) or mojom.IsAnyInterfaceKind(dependency):\n+      assertDependencyIsStable(dependency.kind)\n+    elif mojom.IsMapKind(dependency):\n+      assertDependencyIsStable(dependency.key_kind)\n+      assertDependencyIsStable(dependency.value_kind)\n+\n+  if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):\n+    for field in kind.fields:\n+      assertDependencyIsStable(field.kind)\n+  elif mojom.IsInterfaceKind(kind):\n+    for method in kind.methods:\n+      for param in method.param_struct.fields:\n+        assertDependencyIsStable(param.kind)\n+      if method.response_param_struct:\n+        for response_param in method.response_param_struct.fields:\n+          assertDependencyIsStable(response_param.kind)\n+\n+\n+def _Module(tree, path, imports):\n+  \"\"\"\n+  Args:\n+    tree: {ast.Mojom} The parse tree.\n+    path: {str} The path to the mojom file.\n+    imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in\n+        the import list, to already processed modules. Used to process imports.\n+\n+  Returns:\n+    {mojom.Module} An AST for the mojom.\n+  \"\"\"\n+  module = mojom.Module(path=path)\n+  module.kinds = {}\n+  for kind in mojom.PRIMITIVES:\n+    module.kinds[kind.spec] = kind\n+\n+  module.values = {}\n+\n+  module.mojom_namespace = tree.module.mojom_namespace[1] if tree.module else ''\n+  # Imports must come first, because they add to module.kinds which is used\n+  # by by the others.\n+  module.imports = [\n+      _Import(module, imports[imp.import_filename]) for imp in tree.import_list\n+  ]\n+  if tree.module and tree.module.attribute_list:\n+    assert isinstance(tree.module.attribute_list, ast.AttributeList)\n+    # TODO(vtl): Check for duplicate keys here.\n+    module.attributes = dict((attribute.key, attribute.value)\n+                             for attribute in tree.module.attribute_list)\n+\n+  filename = os.path.basename(path)\n+  # First pass collects kinds.\n+  module.constants = []\n+  module.enums = []\n+  module.structs = []\n+  module.unions = []\n+  module.interfaces = []\n+  _ProcessElements(\n+      filename, tree.definition_list, {\n+          ast.Const:\n+          lambda const: module.constants.append(_Constant(module, const, None)),\n+          ast.Enum:\n+          lambda enum: module.enums.append(_Enum(module, enum, None)),\n+          ast.Struct:\n+          lambda struct: module.structs.append(_Struct(module, struct)),\n+          ast.Union:\n+          lambda union: module.unions.append(_Union(module, union)),\n+          ast.Interface:\n+          lambda interface: module.interfaces.append(\n+              _Interface(module, interface)),\n+      })\n+\n+  # Second pass expands fields and methods. This allows fields and parameters\n+  # to refer to kinds defined anywhere in the mojom.\n+  all_defined_kinds = {}\n+  for struct in module.structs:\n+    struct.fields = list(\n+        map(lambda field: _StructField(module, field, struct),\n+            struct.fields_data))\n+    _AssignDefaultOrdinals(struct.fields)\n+    del struct.fields_data\n+    all_defined_kinds[struct.spec] = struct\n+    for enum in struct.enums:\n+      all_defined_kinds[enum.spec] = enum\n+\n+  for union in module.unions:\n+    union.fields = list(\n+        map(lambda field: _UnionField(module, field, union), union.fields_data))\n+    _AssignDefaultOrdinals(union.fields)\n+    del union.fields_data\n+    all_defined_kinds[union.spec] = union\n+\n+  for interface in module.interfaces:\n+    interface.methods = list(\n+        map(lambda method: _Method(module, method, interface),\n+            interface.methods_data))\n+    _AssignDefaultOrdinals(interface.methods)\n+    del interface.methods_data\n+    all_defined_kinds[interface.spec] = interface\n+    for enum in interface.enums:\n+      all_defined_kinds[enum.spec] = enum\n+  for enum in module.enums:\n+    all_defined_kinds[enum.spec] = enum\n+\n+  all_referenced_kinds = _CollectReferencedKinds(module,\n+                                                 all_defined_kinds.values())\n+  imported_kind_specs = set(all_referenced_kinds.keys()).difference(\n+      set(all_defined_kinds.keys()))\n+  module.imported_kinds = dict(\n+      (spec, all_referenced_kinds[spec]) for spec in imported_kind_specs)\n+\n+  generator.AddComputedData(module)\n+  for iface in module.interfaces:\n+    for method in iface.methods:\n+      if method.param_struct:\n+        _AssignDefaultOrdinals(method.param_struct.fields)\n+      if method.response_param_struct:\n+        _AssignDefaultOrdinals(method.response_param_struct.fields)\n+\n+  # Ensure that all types marked [Stable] are actually stable. Enums are\n+  # automatically OK since they don't depend on other definitions.\n+  for kinds in (module.structs, module.unions, module.interfaces):\n+    for kind in kinds:\n+      if kind.stable:\n+        _AssertTypeIsStable(kind)\n+\n+  return module\n+\n+\n+def OrderedModule(tree, path, imports):\n+  \"\"\"Convert parse tree to AST module.\n+\n+  Args:\n+    tree: {ast.Mojom} The parse tree.\n+    path: {str} The path to the mojom file.\n+    imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in\n+        the import list, to already processed modules. Used to process imports.\n+\n+  Returns:\n+    {mojom.Module} An AST for the mojom.\n+  \"\"\"\n+  module = _Module(tree, path, imports)\n+  return module\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py\nnew file mode 100644\nindex 00000000..19905c8a\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py\n@@ -0,0 +1,73 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import imp\n+import os.path\n+import sys\n+import unittest\n+\n+from mojom.generate import module as mojom\n+from mojom.generate import translate\n+from mojom.parse import ast\n+\n+\n+class TranslateTest(unittest.TestCase):\n+  \"\"\"Tests |parser.Parse()|.\"\"\"\n+\n+  def testSimpleArray(self):\n+    \"\"\"Tests a simple int32[].\"\"\"\n+    # pylint: disable=W0212\n+    self.assertEquals(translate._MapKind(\"int32[]\"), \"a:i32\")\n+\n+  def testAssociativeArray(self):\n+    \"\"\"Tests a simple uint8{string}.\"\"\"\n+    # pylint: disable=W0212\n+    self.assertEquals(translate._MapKind(\"uint8{string}\"), \"m[s][u8]\")\n+\n+  def testLeftToRightAssociativeArray(self):\n+    \"\"\"Makes sure that parsing is done from right to left on the internal kinds\n+       in the presence of an associative array.\"\"\"\n+    # pylint: disable=W0212\n+    self.assertEquals(translate._MapKind(\"uint8[]{string}\"), \"m[s][a:u8]\")\n+\n+  def testTranslateSimpleUnions(self):\n+    \"\"\"Makes sure that a simple union is translated correctly.\"\"\"\n+    tree = ast.Mojom(None, ast.ImportList(), [\n+        ast.Union(\n+            \"SomeUnion\", None,\n+            ast.UnionBody([\n+                ast.UnionField(\"a\", None, None, \"int32\"),\n+                ast.UnionField(\"b\", None, None, \"string\")\n+            ]))\n+    ])\n+\n+    translation = translate.OrderedModule(tree, \"mojom_tree\", [])\n+    self.assertEqual(1, len(translation.unions))\n+\n+    union = translation.unions[0]\n+    self.assertTrue(isinstance(union, mojom.Union))\n+    self.assertEqual(\"SomeUnion\", union.mojom_name)\n+    self.assertEqual(2, len(union.fields))\n+    self.assertEqual(\"a\", union.fields[0].mojom_name)\n+    self.assertEqual(mojom.INT32.spec, union.fields[0].kind.spec)\n+    self.assertEqual(\"b\", union.fields[1].mojom_name)\n+    self.assertEqual(mojom.STRING.spec, union.fields[1].kind.spec)\n+\n+  def testMapKindRaisesWithDuplicate(self):\n+    \"\"\"Verifies _MapTreeForType() raises when passed two values with the same\n+       name.\"\"\"\n+    methods = [\n+        ast.Method('dup', None, None, ast.ParameterList(), None),\n+        ast.Method('dup', None, None, ast.ParameterList(), None)\n+    ]\n+    with self.assertRaises(Exception):\n+      translate._ElemsOfType(methods, ast.Method, 'scope')\n+\n+  def testAssociatedKinds(self):\n+    \"\"\"Tests type spec translation of associated interfaces and requests.\"\"\"\n+    # pylint: disable=W0212\n+    self.assertEquals(\n+        translate._MapKind(\"asso<SomeInterface>?\"), \"?asso:x:SomeInterface\")\n+    self.assertEquals(\n+        translate._MapKind(\"asso<SomeInterface&>?\"), \"?asso:r:x:SomeInterface\")\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/__init__.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/__init__.py\nnew file mode 100644\nindex 00000000..e69de29b\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py\nnew file mode 100644\nindex 00000000..1f0db200\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py\n@@ -0,0 +1,427 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Node classes for the AST for a Mojo IDL file.\"\"\"\n+\n+# Note: For convenience of testing, you probably want to define __eq__() methods\n+# for all node types; it's okay to be slightly lax (e.g., not compare filename\n+# and lineno). You may also define __repr__() to help with analyzing test\n+# failures, especially for more complex types.\n+\n+\n+import sys\n+\n+\n+def _IsStrOrUnicode(x):\n+  if sys.version_info[0] < 3:\n+    return isinstance(x, (unicode, str))\n+  return isinstance(x, str)\n+\n+\n+class NodeBase(object):\n+  \"\"\"Base class for nodes in the AST.\"\"\"\n+\n+  def __init__(self, filename=None, lineno=None):\n+    self.filename = filename\n+    self.lineno = lineno\n+\n+  def __eq__(self, other):\n+    # We want strict comparison of the two object's types. Disable pylint's\n+    # insistence upon recommending isinstance().\n+    # pylint: disable=unidiomatic-typecheck\n+    return type(self) == type(other)\n+\n+  # Make != the inverse of ==. (Subclasses shouldn't have to override this.)\n+  def __ne__(self, other):\n+    return not self == other\n+\n+\n+# TODO(vtl): Some of this is complicated enough that it should be tested.\n+class NodeListBase(NodeBase):\n+  \"\"\"Represents a list of other nodes, all having the same type. (This is meant\n+  to be subclassed, with subclasses defining _list_item_type to be the class (or\n+  classes, in a tuple) of the members of the list.)\"\"\"\n+\n+  def __init__(self, item_or_items=None, **kwargs):\n+    super(NodeListBase, self).__init__(**kwargs)\n+    self.items = []\n+    if item_or_items is None:\n+      pass\n+    elif isinstance(item_or_items, list):\n+      for item in item_or_items:\n+        assert isinstance(item, self._list_item_type)\n+        self.Append(item)\n+    else:\n+      assert isinstance(item_or_items, self._list_item_type)\n+      self.Append(item_or_items)\n+\n+  # Support iteration. For everything else, users should just access |items|\n+  # directly. (We intentionally do NOT supply |__len__()| or |__nonzero__()|, so\n+  # |bool(NodeListBase())| is true.)\n+  def __iter__(self):\n+    return self.items.__iter__()\n+\n+  def __eq__(self, other):\n+    return super(NodeListBase, self).__eq__(other) and \\\n+           self.items == other.items\n+\n+  # Implement this so that on failure, we get slightly more sensible output.\n+  def __repr__(self):\n+    return self.__class__.__name__ + \"([\" + \\\n+           \", \".join([repr(elem) for elem in self.items]) + \"])\"\n+\n+  def Insert(self, item):\n+    \"\"\"Inserts item at the front of the list.\"\"\"\n+\n+    assert isinstance(item, self._list_item_type)\n+    self.items.insert(0, item)\n+    self._UpdateFilenameAndLineno()\n+\n+  def Append(self, item):\n+    \"\"\"Appends item to the end of the list.\"\"\"\n+\n+    assert isinstance(item, self._list_item_type)\n+    self.items.append(item)\n+    self._UpdateFilenameAndLineno()\n+\n+  def _UpdateFilenameAndLineno(self):\n+    if self.items:\n+      self.filename = self.items[0].filename\n+      self.lineno = self.items[0].lineno\n+\n+\n+class Definition(NodeBase):\n+  \"\"\"Represents a definition of anything that has a global name (e.g., enums,\n+  enum values, consts, structs, struct fields, interfaces). (This does not\n+  include parameter definitions.) This class is meant to be subclassed.\"\"\"\n+\n+  def __init__(self, mojom_name, **kwargs):\n+    assert _IsStrOrUnicode(mojom_name)\n+    NodeBase.__init__(self, **kwargs)\n+    self.mojom_name = mojom_name\n+\n+\n+################################################################################\n+\n+\n+class Attribute(NodeBase):\n+  \"\"\"Represents an attribute.\"\"\"\n+\n+  def __init__(self, key, value, **kwargs):\n+    assert _IsStrOrUnicode(key)\n+    super(Attribute, self).__init__(**kwargs)\n+    self.key = key\n+    self.value = value\n+\n+  def __eq__(self, other):\n+    return super(Attribute, self).__eq__(other) and \\\n+           self.key == other.key and \\\n+           self.value == other.value\n+\n+\n+class AttributeList(NodeListBase):\n+  \"\"\"Represents a list attributes.\"\"\"\n+\n+  _list_item_type = Attribute\n+\n+\n+class Const(Definition):\n+  \"\"\"Represents a const definition.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, typename, value, **kwargs):\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    # The typename is currently passed through as a string.\n+    assert _IsStrOrUnicode(typename)\n+    # The value is either a literal (currently passed through as a string) or a\n+    # \"wrapped identifier\".\n+    assert _IsStrOrUnicode or isinstance(value, tuple)\n+    super(Const, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.typename = typename\n+    self.value = value\n+\n+  def __eq__(self, other):\n+    return super(Const, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.typename == other.typename and \\\n+           self.value == other.value\n+\n+\n+class Enum(Definition):\n+  \"\"\"Represents an enum definition.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, enum_value_list, **kwargs):\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert enum_value_list is None or isinstance(enum_value_list, EnumValueList)\n+    super(Enum, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.enum_value_list = enum_value_list\n+\n+  def __eq__(self, other):\n+    return super(Enum, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.enum_value_list == other.enum_value_list\n+\n+\n+class EnumValue(Definition):\n+  \"\"\"Represents a definition of an enum value.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, value, **kwargs):\n+    # The optional value is either an int (which is current a string) or a\n+    # \"wrapped identifier\".\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert value is None or _IsStrOrUnicode(value) or isinstance(value, tuple)\n+    super(EnumValue, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.value = value\n+\n+  def __eq__(self, other):\n+    return super(EnumValue, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.value == other.value\n+\n+\n+class EnumValueList(NodeListBase):\n+  \"\"\"Represents a list of enum value definitions (i.e., the \"body\" of an enum\n+  definition).\"\"\"\n+\n+  _list_item_type = EnumValue\n+\n+\n+class Import(NodeBase):\n+  \"\"\"Represents an import statement.\"\"\"\n+\n+  def __init__(self, attribute_list, import_filename, **kwargs):\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert _IsStrOrUnicode(import_filename)\n+    super(Import, self).__init__(**kwargs)\n+    self.attribute_list = attribute_list\n+    self.import_filename = import_filename\n+\n+  def __eq__(self, other):\n+    return super(Import, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.import_filename == other.import_filename\n+\n+\n+class ImportList(NodeListBase):\n+  \"\"\"Represents a list (i.e., sequence) of import statements.\"\"\"\n+\n+  _list_item_type = Import\n+\n+\n+class Interface(Definition):\n+  \"\"\"Represents an interface definition.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, body, **kwargs):\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert isinstance(body, InterfaceBody)\n+    super(Interface, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.body = body\n+\n+  def __eq__(self, other):\n+    return super(Interface, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.body == other.body\n+\n+\n+class Method(Definition):\n+  \"\"\"Represents a method definition.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, ordinal, parameter_list,\n+               response_parameter_list, **kwargs):\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert ordinal is None or isinstance(ordinal, Ordinal)\n+    assert isinstance(parameter_list, ParameterList)\n+    assert response_parameter_list is None or \\\n+           isinstance(response_parameter_list, ParameterList)\n+    super(Method, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.ordinal = ordinal\n+    self.parameter_list = parameter_list\n+    self.response_parameter_list = response_parameter_list\n+\n+  def __eq__(self, other):\n+    return super(Method, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.ordinal == other.ordinal and \\\n+           self.parameter_list == other.parameter_list and \\\n+           self.response_parameter_list == other.response_parameter_list\n+\n+\n+# This needs to be declared after |Method|.\n+class InterfaceBody(NodeListBase):\n+  \"\"\"Represents the body of (i.e., list of definitions inside) an interface.\"\"\"\n+\n+  _list_item_type = (Const, Enum, Method)\n+\n+\n+class Module(NodeBase):\n+  \"\"\"Represents a module statement.\"\"\"\n+\n+  def __init__(self, mojom_namespace, attribute_list, **kwargs):\n+    # |mojom_namespace| is either none or a \"wrapped identifier\".\n+    assert mojom_namespace is None or isinstance(mojom_namespace, tuple)\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    super(Module, self).__init__(**kwargs)\n+    self.mojom_namespace = mojom_namespace\n+    self.attribute_list = attribute_list\n+\n+  def __eq__(self, other):\n+    return super(Module, self).__eq__(other) and \\\n+           self.mojom_namespace == other.mojom_namespace and \\\n+           self.attribute_list == other.attribute_list\n+\n+\n+class Mojom(NodeBase):\n+  \"\"\"Represents an entire .mojom file. (This is the root node.)\"\"\"\n+\n+  def __init__(self, module, import_list, definition_list, **kwargs):\n+    assert module is None or isinstance(module, Module)\n+    assert isinstance(import_list, ImportList)\n+    assert isinstance(definition_list, list)\n+    super(Mojom, self).__init__(**kwargs)\n+    self.module = module\n+    self.import_list = import_list\n+    self.definition_list = definition_list\n+\n+  def __eq__(self, other):\n+    return super(Mojom, self).__eq__(other) and \\\n+           self.module == other.module and \\\n+           self.import_list == other.import_list and \\\n+           self.definition_list == other.definition_list\n+\n+  def __repr__(self):\n+    return \"%s(%r, %r, %r)\" % (self.__class__.__name__, self.module,\n+                               self.import_list, self.definition_list)\n+\n+\n+class Ordinal(NodeBase):\n+  \"\"\"Represents an ordinal value labeling, e.g., a struct field.\"\"\"\n+\n+  def __init__(self, value, **kwargs):\n+    assert isinstance(value, int)\n+    super(Ordinal, self).__init__(**kwargs)\n+    self.value = value\n+\n+  def __eq__(self, other):\n+    return super(Ordinal, self).__eq__(other) and \\\n+           self.value == other.value\n+\n+\n+class Parameter(NodeBase):\n+  \"\"\"Represents a method request or response parameter.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, ordinal, typename, **kwargs):\n+    assert _IsStrOrUnicode(mojom_name)\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert ordinal is None or isinstance(ordinal, Ordinal)\n+    assert _IsStrOrUnicode(typename)\n+    super(Parameter, self).__init__(**kwargs)\n+    self.mojom_name = mojom_name\n+    self.attribute_list = attribute_list\n+    self.ordinal = ordinal\n+    self.typename = typename\n+\n+  def __eq__(self, other):\n+    return super(Parameter, self).__eq__(other) and \\\n+           self.mojom_name == other.mojom_name and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.ordinal == other.ordinal and \\\n+           self.typename == other.typename\n+\n+\n+class ParameterList(NodeListBase):\n+  \"\"\"Represents a list of (method request or response) parameters.\"\"\"\n+\n+  _list_item_type = Parameter\n+\n+\n+class Struct(Definition):\n+  \"\"\"Represents a struct definition.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, body, **kwargs):\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert isinstance(body, StructBody) or body is None\n+    super(Struct, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.body = body\n+\n+  def __eq__(self, other):\n+    return super(Struct, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.body == other.body\n+\n+\n+class StructField(Definition):\n+  \"\"\"Represents a struct field definition.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, ordinal, typename,\n+               default_value, **kwargs):\n+    assert _IsStrOrUnicode(mojom_name)\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert ordinal is None or isinstance(ordinal, Ordinal)\n+    assert _IsStrOrUnicode(typename)\n+    # The optional default value is currently either a value as a string or a\n+    # \"wrapped identifier\".\n+    assert default_value is None or _IsStrOrUnicode(default_value) or \\\n+        isinstance(default_value, tuple)\n+    super(StructField, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.ordinal = ordinal\n+    self.typename = typename\n+    self.default_value = default_value\n+\n+  def __eq__(self, other):\n+    return super(StructField, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.ordinal == other.ordinal and \\\n+           self.typename == other.typename and \\\n+           self.default_value == other.default_value\n+\n+\n+# This needs to be declared after |StructField|.\n+class StructBody(NodeListBase):\n+  \"\"\"Represents the body of (i.e., list of definitions inside) a struct.\"\"\"\n+\n+  _list_item_type = (Const, Enum, StructField)\n+\n+\n+class Union(Definition):\n+  \"\"\"Represents a union definition.\"\"\"\n+\n+  def __init__(self, mojom_name, attribute_list, body, **kwargs):\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert isinstance(body, UnionBody)\n+    super(Union, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.body = body\n+\n+  def __eq__(self, other):\n+    return super(Union, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.body == other.body\n+\n+\n+class UnionField(Definition):\n+  def __init__(self, mojom_name, attribute_list, ordinal, typename, **kwargs):\n+    assert _IsStrOrUnicode(mojom_name)\n+    assert attribute_list is None or isinstance(attribute_list, AttributeList)\n+    assert ordinal is None or isinstance(ordinal, Ordinal)\n+    assert _IsStrOrUnicode(typename)\n+    super(UnionField, self).__init__(mojom_name, **kwargs)\n+    self.attribute_list = attribute_list\n+    self.ordinal = ordinal\n+    self.typename = typename\n+\n+  def __eq__(self, other):\n+    return super(UnionField, self).__eq__(other) and \\\n+           self.attribute_list == other.attribute_list and \\\n+           self.ordinal == other.ordinal and \\\n+           self.typename == other.typename\n+\n+\n+class UnionBody(NodeListBase):\n+\n+  _list_item_type = UnionField\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py\nnew file mode 100644\nindex 00000000..62798631\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py\n@@ -0,0 +1,121 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import imp\n+import os.path\n+import sys\n+import unittest\n+\n+from mojom.parse import ast\n+\n+\n+class _TestNode(ast.NodeBase):\n+  \"\"\"Node type for tests.\"\"\"\n+\n+  def __init__(self, value, **kwargs):\n+    super(_TestNode, self).__init__(**kwargs)\n+    self.value = value\n+\n+  def __eq__(self, other):\n+    return super(_TestNode, self).__eq__(other) and self.value == other.value\n+\n+\n+class _TestNodeList(ast.NodeListBase):\n+  \"\"\"Node list type for tests.\"\"\"\n+\n+  _list_item_type = _TestNode\n+\n+\n+class ASTTest(unittest.TestCase):\n+  \"\"\"Tests various AST classes.\"\"\"\n+\n+  def testNodeBase(self):\n+    # Test |__eq__()|; this is only used for testing, where we want to do\n+    # comparison by value and ignore filenames/line numbers (for convenience).\n+    node1 = ast.NodeBase(filename=\"hello.mojom\", lineno=123)\n+    node2 = ast.NodeBase()\n+    self.assertEquals(node1, node2)\n+    self.assertEquals(node2, node1)\n+\n+    # Check that |__ne__()| just defers to |__eq__()| properly.\n+    self.assertFalse(node1 != node2)\n+    self.assertFalse(node2 != node1)\n+\n+    # Check that |filename| and |lineno| are set properly (and are None by\n+    # default).\n+    self.assertEquals(node1.filename, \"hello.mojom\")\n+    self.assertEquals(node1.lineno, 123)\n+    self.assertIsNone(node2.filename)\n+    self.assertIsNone(node2.lineno)\n+\n+    # |NodeBase|'s |__eq__()| should compare types (and a subclass's |__eq__()|\n+    # should first defer to its superclass's).\n+    node3 = _TestNode(123)\n+    self.assertNotEqual(node1, node3)\n+    self.assertNotEqual(node3, node1)\n+    # Also test |__eq__()| directly.\n+    self.assertFalse(node1 == node3)\n+    self.assertFalse(node3 == node1)\n+\n+    node4 = _TestNode(123, filename=\"world.mojom\", lineno=123)\n+    self.assertEquals(node4, node3)\n+    node5 = _TestNode(456)\n+    self.assertNotEquals(node5, node4)\n+\n+  def testNodeListBase(self):\n+    node1 = _TestNode(1, filename=\"foo.mojom\", lineno=1)\n+    # Equal to, but not the same as, |node1|:\n+    node1b = _TestNode(1, filename=\"foo.mojom\", lineno=1)\n+    node2 = _TestNode(2, filename=\"foo.mojom\", lineno=2)\n+\n+    nodelist1 = _TestNodeList()  # Contains: (empty).\n+    self.assertEquals(nodelist1, nodelist1)\n+    self.assertEquals(nodelist1.items, [])\n+    self.assertIsNone(nodelist1.filename)\n+    self.assertIsNone(nodelist1.lineno)\n+\n+    nodelist2 = _TestNodeList(node1)  # Contains: 1.\n+    self.assertEquals(nodelist2, nodelist2)\n+    self.assertEquals(nodelist2.items, [node1])\n+    self.assertNotEqual(nodelist2, nodelist1)\n+    self.assertEquals(nodelist2.filename, \"foo.mojom\")\n+    self.assertEquals(nodelist2.lineno, 1)\n+\n+    nodelist3 = _TestNodeList([node2])  # Contains: 2.\n+    self.assertEquals(nodelist3.items, [node2])\n+    self.assertNotEqual(nodelist3, nodelist1)\n+    self.assertNotEqual(nodelist3, nodelist2)\n+    self.assertEquals(nodelist3.filename, \"foo.mojom\")\n+    self.assertEquals(nodelist3.lineno, 2)\n+\n+    nodelist1.Append(node1b)  # Contains: 1.\n+    self.assertEquals(nodelist1.items, [node1])\n+    self.assertEquals(nodelist1, nodelist2)\n+    self.assertNotEqual(nodelist1, nodelist3)\n+    self.assertEquals(nodelist1.filename, \"foo.mojom\")\n+    self.assertEquals(nodelist1.lineno, 1)\n+\n+    nodelist1.Append(node2)  # Contains: 1, 2.\n+    self.assertEquals(nodelist1.items, [node1, node2])\n+    self.assertNotEqual(nodelist1, nodelist2)\n+    self.assertNotEqual(nodelist1, nodelist3)\n+    self.assertEquals(nodelist1.lineno, 1)\n+\n+    nodelist2.Append(node2)  # Contains: 1, 2.\n+    self.assertEquals(nodelist2.items, [node1, node2])\n+    self.assertEquals(nodelist2, nodelist1)\n+    self.assertNotEqual(nodelist2, nodelist3)\n+    self.assertEquals(nodelist2.lineno, 1)\n+\n+    nodelist3.Insert(node1)  # Contains: 1, 2.\n+    self.assertEquals(nodelist3.items, [node1, node2])\n+    self.assertEquals(nodelist3, nodelist1)\n+    self.assertEquals(nodelist3, nodelist2)\n+    self.assertEquals(nodelist3.lineno, 1)\n+\n+    # Test iteration:\n+    i = 1\n+    for item in nodelist1:\n+      self.assertEquals(item.value, i)\n+      i += 1\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py\nnew file mode 100644\nindex 00000000..3cb73c5d\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py\n@@ -0,0 +1,82 @@\n+# Copyright 2018 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Helpers for processing conditionally enabled features in a mojom.\"\"\"\n+\n+from mojom.error import Error\n+from mojom.parse import ast\n+\n+\n+class EnableIfError(Error):\n+  \"\"\" Class for errors from .\"\"\"\n+\n+  def __init__(self, filename, message, lineno=None):\n+    Error.__init__(self, filename, message, lineno=lineno, addenda=None)\n+\n+\n+def _IsEnabled(definition, enabled_features):\n+  \"\"\"Returns true if a definition is enabled.\n+\n+  A definition is enabled if it has no EnableIf attribute, or if the value of\n+  the EnableIf attribute is in enabled_features.\n+  \"\"\"\n+  if not hasattr(definition, \"attribute_list\"):\n+    return True\n+  if not definition.attribute_list:\n+    return True\n+\n+  already_defined = False\n+  for a in definition.attribute_list:\n+    if a.key == 'EnableIf':\n+      if already_defined:\n+        raise EnableIfError(\n+            definition.filename,\n+            \"EnableIf attribute may only be defined once per field.\",\n+            definition.lineno)\n+      already_defined = True\n+\n+  for attribute in definition.attribute_list:\n+    if attribute.key == 'EnableIf' and attribute.value not in enabled_features:\n+      return False\n+  return True\n+\n+\n+def _FilterDisabledFromNodeList(node_list, enabled_features):\n+  if not node_list:\n+    return\n+  assert isinstance(node_list, ast.NodeListBase)\n+  node_list.items = [\n+      item for item in node_list.items if _IsEnabled(item, enabled_features)\n+  ]\n+  for item in node_list.items:\n+    _FilterDefinition(item, enabled_features)\n+\n+\n+def _FilterDefinition(definition, enabled_features):\n+  \"\"\"Filters definitions with a body.\"\"\"\n+  if isinstance(definition, ast.Enum):\n+    _FilterDisabledFromNodeList(definition.enum_value_list, enabled_features)\n+  elif isinstance(definition, ast.Interface):\n+    _FilterDisabledFromNodeList(definition.body, enabled_features)\n+  elif isinstance(definition, ast.Method):\n+    _FilterDisabledFromNodeList(definition.parameter_list, enabled_features)\n+    _FilterDisabledFromNodeList(definition.response_parameter_list,\n+                                enabled_features)\n+  elif isinstance(definition, ast.Struct):\n+    _FilterDisabledFromNodeList(definition.body, enabled_features)\n+  elif isinstance(definition, ast.Union):\n+    _FilterDisabledFromNodeList(definition.body, enabled_features)\n+\n+\n+def RemoveDisabledDefinitions(mojom, enabled_features):\n+  \"\"\"Removes conditionally disabled definitions from a Mojom node.\"\"\"\n+  mojom.import_list = ast.ImportList([\n+      imported_file for imported_file in mojom.import_list\n+      if _IsEnabled(imported_file, enabled_features)\n+  ])\n+  mojom.definition_list = [\n+      definition for definition in mojom.definition_list\n+      if _IsEnabled(definition, enabled_features)\n+  ]\n+  for definition in mojom.definition_list:\n+    _FilterDefinition(definition, enabled_features)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py\nnew file mode 100644\nindex 00000000..aa609be7\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py\n@@ -0,0 +1,233 @@\n+# Copyright 2018 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import imp\n+import os\n+import sys\n+import unittest\n+\n+\n+def _GetDirAbove(dirname):\n+  \"\"\"Returns the directory \"above\" this file containing |dirname| (which must\n+  also be \"above\" this file).\"\"\"\n+  path = os.path.abspath(__file__)\n+  while True:\n+    path, tail = os.path.split(path)\n+    assert tail\n+    if tail == dirname:\n+      return path\n+\n+\n+try:\n+  imp.find_module('mojom')\n+except ImportError:\n+  sys.path.append(os.path.join(_GetDirAbove('pylib'), 'pylib'))\n+import mojom.parse.ast as ast\n+import mojom.parse.conditional_features as conditional_features\n+import mojom.parse.parser as parser\n+\n+ENABLED_FEATURES = frozenset({'red', 'green', 'blue'})\n+\n+\n+class ConditionalFeaturesTest(unittest.TestCase):\n+  \"\"\"Tests |mojom.parse.conditional_features|.\"\"\"\n+\n+  def parseAndAssertEqual(self, source, expected_source):\n+    definition = parser.Parse(source, \"my_file.mojom\")\n+    conditional_features.RemoveDisabledDefinitions(definition, ENABLED_FEATURES)\n+    expected = parser.Parse(expected_source, \"my_file.mojom\")\n+    self.assertEquals(definition, expected)\n+\n+  def testFilterConst(self):\n+    \"\"\"Test that Consts are correctly filtered.\"\"\"\n+    const_source = \"\"\"\n+      [EnableIf=blue]\n+      const int kMyConst1 = 1;\n+      [EnableIf=orange]\n+      const double kMyConst2 = 2;\n+      const int kMyConst3 = 3;\n+    \"\"\"\n+    expected_source = \"\"\"\n+      [EnableIf=blue]\n+      const int kMyConst1 = 1;\n+      const int kMyConst3 = 3;\n+    \"\"\"\n+    self.parseAndAssertEqual(const_source, expected_source)\n+\n+  def testFilterEnum(self):\n+    \"\"\"Test that EnumValues are correctly filtered from an Enum.\"\"\"\n+    enum_source = \"\"\"\n+      enum MyEnum {\n+        [EnableIf=purple]\n+        VALUE1,\n+        [EnableIf=blue]\n+        VALUE2,\n+        VALUE3,\n+      };\n+    \"\"\"\n+    expected_source = \"\"\"\n+      enum MyEnum {\n+        [EnableIf=blue]\n+        VALUE2,\n+        VALUE3\n+      };\n+    \"\"\"\n+    self.parseAndAssertEqual(enum_source, expected_source)\n+\n+  def testFilterImport(self):\n+    \"\"\"Test that imports are correctly filtered from a Mojom.\"\"\"\n+    import_source = \"\"\"\n+      [EnableIf=blue]\n+      import \"foo.mojom\";\n+      import \"bar.mojom\";\n+      [EnableIf=purple]\n+      import \"baz.mojom\";\n+    \"\"\"\n+    expected_source = \"\"\"\n+      [EnableIf=blue]\n+      import \"foo.mojom\";\n+      import \"bar.mojom\";\n+    \"\"\"\n+    self.parseAndAssertEqual(import_source, expected_source)\n+\n+  def testFilterInterface(self):\n+    \"\"\"Test that definitions are correctly filtered from an Interface.\"\"\"\n+    interface_source = \"\"\"\n+      interface MyInterface {\n+        [EnableIf=blue]\n+        enum MyEnum {\n+          [EnableIf=purple]\n+          VALUE1,\n+          VALUE2,\n+        };\n+        [EnableIf=blue]\n+        const int32 kMyConst = 123;\n+        [EnableIf=purple]\n+        MyMethod();\n+      };\n+    \"\"\"\n+    expected_source = \"\"\"\n+      interface MyInterface {\n+        [EnableIf=blue]\n+        enum MyEnum {\n+          VALUE2,\n+        };\n+        [EnableIf=blue]\n+        const int32 kMyConst = 123;\n+      };\n+    \"\"\"\n+    self.parseAndAssertEqual(interface_source, expected_source)\n+\n+  def testFilterMethod(self):\n+    \"\"\"Test that Parameters are correctly filtered from a Method.\"\"\"\n+    method_source = \"\"\"\n+      interface MyInterface {\n+        [EnableIf=blue]\n+        MyMethod([EnableIf=purple] int32 a) => ([EnableIf=red] int32 b);\n+      };\n+    \"\"\"\n+    expected_source = \"\"\"\n+      interface MyInterface {\n+        [EnableIf=blue]\n+        MyMethod() => ([EnableIf=red] int32 b);\n+      };\n+    \"\"\"\n+    self.parseAndAssertEqual(method_source, expected_source)\n+\n+  def testFilterStruct(self):\n+    \"\"\"Test that definitions are correctly filtered from a Struct.\"\"\"\n+    struct_source = \"\"\"\n+      struct MyStruct {\n+        [EnableIf=blue]\n+        enum MyEnum {\n+          VALUE1,\n+          [EnableIf=purple]\n+          VALUE2,\n+        };\n+        [EnableIf=yellow]\n+        const double kMyConst = 1.23;\n+        [EnableIf=green]\n+        int32 a;\n+        double b;\n+        [EnableIf=purple]\n+        int32 c;\n+        [EnableIf=blue]\n+        double d;\n+        int32 e;\n+        [EnableIf=orange]\n+        double f;\n+      };\n+    \"\"\"\n+    expected_source = \"\"\"\n+      struct MyStruct {\n+        [EnableIf=blue]\n+        enum MyEnum {\n+          VALUE1,\n+        };\n+        [EnableIf=green]\n+        int32 a;\n+        double b;\n+        [EnableIf=blue]\n+        double d;\n+        int32 e;\n+      };\n+    \"\"\"\n+    self.parseAndAssertEqual(struct_source, expected_source)\n+\n+  def testFilterUnion(self):\n+    \"\"\"Test that UnionFields are correctly filtered from a Union.\"\"\"\n+    union_source = \"\"\"\n+      union MyUnion {\n+        [EnableIf=yellow]\n+        int32 a;\n+        [EnableIf=red]\n+        bool b;\n+      };\n+    \"\"\"\n+    expected_source = \"\"\"\n+      union MyUnion {\n+        [EnableIf=red]\n+        bool b;\n+      };\n+    \"\"\"\n+    self.parseAndAssertEqual(union_source, expected_source)\n+\n+  def testSameNameFields(self):\n+    mojom_source = \"\"\"\n+      enum Foo {\n+        [EnableIf=red]\n+        VALUE1 = 5,\n+        [EnableIf=yellow]\n+        VALUE1 = 6,\n+      };\n+      [EnableIf=red]\n+      const double kMyConst = 1.23;\n+      [EnableIf=yellow]\n+      const double kMyConst = 4.56;\n+    \"\"\"\n+    expected_source = \"\"\"\n+      enum Foo {\n+        [EnableIf=red]\n+        VALUE1 = 5,\n+      };\n+      [EnableIf=red]\n+      const double kMyConst = 1.23;\n+    \"\"\"\n+    self.parseAndAssertEqual(mojom_source, expected_source)\n+\n+  def testMultipleEnableIfs(self):\n+    source = \"\"\"\n+      enum Foo {\n+        [EnableIf=red,EnableIf=yellow]\n+        kBarValue = 5,\n+      };\n+    \"\"\"\n+    definition = parser.Parse(source, \"my_file.mojom\")\n+    self.assertRaises(conditional_features.EnableIfError,\n+                      conditional_features.RemoveDisabledDefinitions,\n+                      definition, ENABLED_FEATURES)\n+\n+\n+if __name__ == '__main__':\n+  unittest.main()\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py\nnew file mode 100644\nindex 00000000..3e084bbf\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py\n@@ -0,0 +1,251 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import imp\n+import os.path\n+import sys\n+\n+from mojom import fileutil\n+from mojom.error import Error\n+\n+fileutil.AddLocalRepoThirdPartyDirToModulePath()\n+from ply.lex import TOKEN\n+\n+\n+class LexError(Error):\n+  \"\"\"Class for errors from the lexer.\"\"\"\n+\n+  def __init__(self, filename, message, lineno):\n+    Error.__init__(self, filename, message, lineno=lineno)\n+\n+\n+# We have methods which look like they could be functions:\n+# pylint: disable=R0201\n+class Lexer(object):\n+  def __init__(self, filename):\n+    self.filename = filename\n+\n+  ######################--   PRIVATE   --######################\n+\n+  ##\n+  ## Internal auxiliary methods\n+  ##\n+  def _error(self, msg, token):\n+    raise LexError(self.filename, msg, token.lineno)\n+\n+  ##\n+  ## Reserved keywords\n+  ##\n+  keywords = (\n+      'HANDLE',\n+      'IMPORT',\n+      'MODULE',\n+      'STRUCT',\n+      'UNION',\n+      'INTERFACE',\n+      'ENUM',\n+      'CONST',\n+      'TRUE',\n+      'FALSE',\n+      'DEFAULT',\n+      'ARRAY',\n+      'MAP',\n+      'ASSOCIATED',\n+      'PENDING_REMOTE',\n+      'PENDING_RECEIVER',\n+      'PENDING_ASSOCIATED_REMOTE',\n+      'PENDING_ASSOCIATED_RECEIVER',\n+  )\n+\n+  keyword_map = {}\n+  for keyword in keywords:\n+    keyword_map[keyword.lower()] = keyword\n+\n+  ##\n+  ## All the tokens recognized by the lexer\n+  ##\n+  tokens = keywords + (\n+      # Identifiers\n+      'NAME',\n+\n+      # Constants\n+      'ORDINAL',\n+      'INT_CONST_DEC',\n+      'INT_CONST_HEX',\n+      'FLOAT_CONST',\n+\n+      # String literals\n+      'STRING_LITERAL',\n+\n+      # Operators\n+      'MINUS',\n+      'PLUS',\n+      'AMP',\n+      'QSTN',\n+\n+      # Assignment\n+      'EQUALS',\n+\n+      # Request / response\n+      'RESPONSE',\n+\n+      # Delimiters\n+      'LPAREN',\n+      'RPAREN',  # ( )\n+      'LBRACKET',\n+      'RBRACKET',  # [ ]\n+      'LBRACE',\n+      'RBRACE',  # { }\n+      'LANGLE',\n+      'RANGLE',  # < >\n+      'SEMI',  # ;\n+      'COMMA',\n+      'DOT'  # , .\n+  )\n+\n+  ##\n+  ## Regexes for use in tokens\n+  ##\n+\n+  # valid C identifiers (K&R2: A.2.3)\n+  identifier = r'[a-zA-Z_][0-9a-zA-Z_]*'\n+\n+  hex_prefix = '0[xX]'\n+  hex_digits = '[0-9a-fA-F]+'\n+\n+  # integer constants (K&R2: A.2.5.1)\n+  decimal_constant = '0|([1-9][0-9]*)'\n+  hex_constant = hex_prefix + hex_digits\n+  # Don't allow octal constants (even invalid octal).\n+  octal_constant_disallowed = '0[0-9]+'\n+\n+  # character constants (K&R2: A.2.5.2)\n+  # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line\n+  # directives with Windows paths as filenames (..\\..\\dir\\file)\n+  # For the same reason, decimal_escape allows all digit sequences. We want to\n+  # parse all correct code, even if it means to sometimes parse incorrect\n+  # code.\n+  #\n+  simple_escape = r\"\"\"([a-zA-Z._~!=&\\^\\-\\\\?'\"])\"\"\"\n+  decimal_escape = r\"\"\"(\\d+)\"\"\"\n+  hex_escape = r\"\"\"(x[0-9a-fA-F]+)\"\"\"\n+  bad_escape = r\"\"\"([\\\\][^a-zA-Z._~^!=&\\^\\-\\\\?'\"x0-7])\"\"\"\n+\n+  escape_sequence = \\\n+      r\"\"\"(\\\\(\"\"\"+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))'\n+\n+  # string literals (K&R2: A.2.6)\n+  string_char = r\"\"\"([^\"\\\\\\n]|\"\"\" + escape_sequence + ')'\n+  string_literal = '\"' + string_char + '*\"'\n+  bad_string_literal = '\"' + string_char + '*' + bad_escape + string_char + '*\"'\n+\n+  # floating constants (K&R2: A.2.5.3)\n+  exponent_part = r\"\"\"([eE][-+]?[0-9]+)\"\"\"\n+  fractional_constant = r\"\"\"([0-9]*\\.[0-9]+)|([0-9]+\\.)\"\"\"\n+  floating_constant = \\\n+      '(((('+fractional_constant+')'+ \\\n+      exponent_part+'?)|([0-9]+'+exponent_part+')))'\n+\n+  # Ordinals\n+  ordinal = r'@[0-9]+'\n+  missing_ordinal_value = r'@'\n+  # Don't allow ordinal values in octal (even invalid octal, like 09) or\n+  # hexadecimal.\n+  octal_or_hex_ordinal_disallowed = (\n+      r'@((0[0-9]+)|(' + hex_prefix + hex_digits + '))')\n+\n+  ##\n+  ## Rules for the normal state\n+  ##\n+  t_ignore = ' \\t\\r'\n+\n+  # Newlines\n+  def t_NEWLINE(self, t):\n+    r'\\n+'\n+    t.lexer.lineno += len(t.value)\n+\n+  # Operators\n+  t_MINUS = r'-'\n+  t_PLUS = r'\\+'\n+  t_AMP = r'&'\n+  t_QSTN = r'\\?'\n+\n+  # =\n+  t_EQUALS = r'='\n+\n+  # =>\n+  t_RESPONSE = r'=>'\n+\n+  # Delimiters\n+  t_LPAREN = r'\\('\n+  t_RPAREN = r'\\)'\n+  t_LBRACKET = r'\\['\n+  t_RBRACKET = r'\\]'\n+  t_LBRACE = r'\\{'\n+  t_RBRACE = r'\\}'\n+  t_LANGLE = r'<'\n+  t_RANGLE = r'>'\n+  t_COMMA = r','\n+  t_DOT = r'\\.'\n+  t_SEMI = r';'\n+\n+  t_STRING_LITERAL = string_literal\n+\n+  # The following floating and integer constants are defined as\n+  # functions to impose a strict order (otherwise, decimal\n+  # is placed before the others because its regex is longer,\n+  # and this is bad)\n+  #\n+  @TOKEN(floating_constant)\n+  def t_FLOAT_CONST(self, t):\n+    return t\n+\n+  @TOKEN(hex_constant)\n+  def t_INT_CONST_HEX(self, t):\n+    return t\n+\n+  @TOKEN(octal_constant_disallowed)\n+  def t_OCTAL_CONSTANT_DISALLOWED(self, t):\n+    msg = \"Octal values not allowed\"\n+    self._error(msg, t)\n+\n+  @TOKEN(decimal_constant)\n+  def t_INT_CONST_DEC(self, t):\n+    return t\n+\n+  # unmatched string literals are caught by the preprocessor\n+\n+  @TOKEN(bad_string_literal)\n+  def t_BAD_STRING_LITERAL(self, t):\n+    msg = \"String contains invalid escape code\"\n+    self._error(msg, t)\n+\n+  # Handle ordinal-related tokens in the right order:\n+  @TOKEN(octal_or_hex_ordinal_disallowed)\n+  def t_OCTAL_OR_HEX_ORDINAL_DISALLOWED(self, t):\n+    msg = \"Octal and hexadecimal ordinal values not allowed\"\n+    self._error(msg, t)\n+\n+  @TOKEN(ordinal)\n+  def t_ORDINAL(self, t):\n+    return t\n+\n+  @TOKEN(missing_ordinal_value)\n+  def t_BAD_ORDINAL(self, t):\n+    msg = \"Missing ordinal value\"\n+    self._error(msg, t)\n+\n+  @TOKEN(identifier)\n+  def t_NAME(self, t):\n+    t.type = self.keyword_map.get(t.value, \"NAME\")\n+    return t\n+\n+  # Ignore C and C++ style comments\n+  def t_COMMENT(self, t):\n+    r'(/\\*(.|\\n)*?\\*/)|(//.*(\\n[ \\t]*//.*)*)'\n+    t.lexer.lineno += t.value.count(\"\\n\")\n+\n+  def t_error(self, t):\n+    msg = \"Illegal character %s\" % repr(t.value[0])\n+    self._error(msg, t)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py\nnew file mode 100644\nindex 00000000..eadc6587\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py\n@@ -0,0 +1,198 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import imp\n+import os.path\n+import sys\n+import unittest\n+\n+\n+def _GetDirAbove(dirname):\n+  \"\"\"Returns the directory \"above\" this file containing |dirname| (which must\n+  also be \"above\" this file).\"\"\"\n+  path = os.path.abspath(__file__)\n+  while True:\n+    path, tail = os.path.split(path)\n+    assert tail\n+    if tail == dirname:\n+      return path\n+\n+\n+sys.path.insert(1, os.path.join(_GetDirAbove(\"mojo\"), \"third_party\"))\n+from ply import lex\n+\n+try:\n+  imp.find_module(\"mojom\")\n+except ImportError:\n+  sys.path.append(os.path.join(_GetDirAbove(\"pylib\"), \"pylib\"))\n+import mojom.parse.lexer\n+\n+\n+# This (monkey-patching LexToken to make comparison value-based) is evil, but\n+# we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing\n+# for object identity.)\n+def _LexTokenEq(self, other):\n+  return self.type == other.type and self.value == other.value and \\\n+         self.lineno == other.lineno and self.lexpos == other.lexpos\n+\n+\n+setattr(lex.LexToken, '__eq__', _LexTokenEq)\n+\n+\n+def _MakeLexToken(token_type, value, lineno=1, lexpos=0):\n+  \"\"\"Makes a LexToken with the given parameters. (Note that lineno is 1-based,\n+  but lexpos is 0-based.)\"\"\"\n+  rv = lex.LexToken()\n+  rv.type, rv.value, rv.lineno, rv.lexpos = token_type, value, lineno, lexpos\n+  return rv\n+\n+\n+def _MakeLexTokenForKeyword(keyword, **kwargs):\n+  \"\"\"Makes a LexToken for the given keyword.\"\"\"\n+  return _MakeLexToken(keyword.upper(), keyword.lower(), **kwargs)\n+\n+\n+class LexerTest(unittest.TestCase):\n+  \"\"\"Tests |mojom.parse.lexer.Lexer|.\"\"\"\n+\n+  def __init__(self, *args, **kwargs):\n+    unittest.TestCase.__init__(self, *args, **kwargs)\n+    # Clone all lexer instances from this one, since making a lexer is slow.\n+    self._zygote_lexer = lex.lex(mojom.parse.lexer.Lexer(\"my_file.mojom\"))\n+\n+  def testValidKeywords(self):\n+    \"\"\"Tests valid keywords.\"\"\"\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"handle\"), _MakeLexTokenForKeyword(\"handle\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"import\"), _MakeLexTokenForKeyword(\"import\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"module\"), _MakeLexTokenForKeyword(\"module\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"struct\"), _MakeLexTokenForKeyword(\"struct\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"union\"), _MakeLexTokenForKeyword(\"union\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"interface\"),\n+        _MakeLexTokenForKeyword(\"interface\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"enum\"), _MakeLexTokenForKeyword(\"enum\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"const\"), _MakeLexTokenForKeyword(\"const\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"true\"), _MakeLexTokenForKeyword(\"true\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"false\"), _MakeLexTokenForKeyword(\"false\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"default\"),\n+        _MakeLexTokenForKeyword(\"default\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"array\"), _MakeLexTokenForKeyword(\"array\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"map\"), _MakeLexTokenForKeyword(\"map\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"associated\"),\n+        _MakeLexTokenForKeyword(\"associated\"))\n+\n+  def testValidIdentifiers(self):\n+    \"\"\"Tests identifiers.\"\"\"\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"abcd\"), _MakeLexToken(\"NAME\", \"abcd\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"AbC_d012_\"),\n+        _MakeLexToken(\"NAME\", \"AbC_d012_\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"_0123\"), _MakeLexToken(\"NAME\", \"_0123\"))\n+\n+  def testInvalidIdentifiers(self):\n+    with self.assertRaisesRegexp(\n+        mojom.parse.lexer.LexError,\n+        r\"^my_file\\.mojom:1: Error: Illegal character '\\$'$\"):\n+      self._TokensForInput(\"$abc\")\n+    with self.assertRaisesRegexp(\n+        mojom.parse.lexer.LexError,\n+        r\"^my_file\\.mojom:1: Error: Illegal character '\\$'$\"):\n+      self._TokensForInput(\"a$bc\")\n+\n+  def testDecimalIntegerConstants(self):\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"0\"), _MakeLexToken(\"INT_CONST_DEC\", \"0\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"1\"), _MakeLexToken(\"INT_CONST_DEC\", \"1\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"123\"), _MakeLexToken(\"INT_CONST_DEC\", \"123\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"10\"), _MakeLexToken(\"INT_CONST_DEC\", \"10\"))\n+\n+  def testValidTokens(self):\n+    \"\"\"Tests valid tokens (which aren't tested elsewhere).\"\"\"\n+    # Keywords tested in |testValidKeywords|.\n+    # NAME tested in |testValidIdentifiers|.\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"@123\"), _MakeLexToken(\"ORDINAL\", \"@123\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"456\"), _MakeLexToken(\"INT_CONST_DEC\", \"456\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"0x01aB2eF3\"),\n+        _MakeLexToken(\"INT_CONST_HEX\", \"0x01aB2eF3\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"123.456\"),\n+        _MakeLexToken(\"FLOAT_CONST\", \"123.456\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"\\\"hello\\\"\"),\n+        _MakeLexToken(\"STRING_LITERAL\", \"\\\"hello\\\"\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"+\"), _MakeLexToken(\"PLUS\", \"+\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"-\"), _MakeLexToken(\"MINUS\", \"-\"))\n+    self.assertEquals(self._SingleTokenForInput(\"&\"), _MakeLexToken(\"AMP\", \"&\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"?\"), _MakeLexToken(\"QSTN\", \"?\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"=\"), _MakeLexToken(\"EQUALS\", \"=\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"=>\"), _MakeLexToken(\"RESPONSE\", \"=>\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"(\"), _MakeLexToken(\"LPAREN\", \"(\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\")\"), _MakeLexToken(\"RPAREN\", \")\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"[\"), _MakeLexToken(\"LBRACKET\", \"[\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"]\"), _MakeLexToken(\"RBRACKET\", \"]\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"{\"), _MakeLexToken(\"LBRACE\", \"{\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"}\"), _MakeLexToken(\"RBRACE\", \"}\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\"<\"), _MakeLexToken(\"LANGLE\", \"<\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\">\"), _MakeLexToken(\"RANGLE\", \">\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\";\"), _MakeLexToken(\"SEMI\", \";\"))\n+    self.assertEquals(\n+        self._SingleTokenForInput(\",\"), _MakeLexToken(\"COMMA\", \",\"))\n+    self.assertEquals(self._SingleTokenForInput(\".\"), _MakeLexToken(\"DOT\", \".\"))\n+\n+  def _TokensForInput(self, input_string):\n+    \"\"\"Gets a list of tokens for the given input string.\"\"\"\n+    lexer = self._zygote_lexer.clone()\n+    lexer.input(input_string)\n+    rv = []\n+    while True:\n+      tok = lexer.token()\n+      if not tok:\n+        return rv\n+      rv.append(tok)\n+\n+  def _SingleTokenForInput(self, input_string):\n+    \"\"\"Gets the single token for the given input string. (Raises an exception if\n+    the input string does not result in exactly one token.)\"\"\"\n+    toks = self._TokensForInput(input_string)\n+    assert len(toks) == 1\n+    return toks[0]\n+\n+\n+if __name__ == \"__main__\":\n+  unittest.main()\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py\nnew file mode 100644\nindex 00000000..b3b803d6\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py\n@@ -0,0 +1,488 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Generates a syntax tree from a Mojo IDL file.\"\"\"\n+\n+import os.path\n+import sys\n+\n+from mojom import fileutil\n+from mojom.error import Error\n+from mojom.parse import ast\n+from mojom.parse.lexer import Lexer\n+\n+fileutil.AddLocalRepoThirdPartyDirToModulePath()\n+from ply import lex\n+from ply import yacc\n+\n+_MAX_ORDINAL_VALUE = 0xffffffff\n+_MAX_ARRAY_SIZE = 0xffffffff\n+\n+\n+class ParseError(Error):\n+  \"\"\"Class for errors from the parser.\"\"\"\n+\n+  def __init__(self, filename, message, lineno=None, snippet=None):\n+    Error.__init__(\n+        self,\n+        filename,\n+        message,\n+        lineno=lineno,\n+        addenda=([snippet] if snippet else None))\n+\n+\n+# We have methods which look like they could be functions:\n+# pylint: disable=R0201\n+class Parser(object):\n+  def __init__(self, lexer, source, filename):\n+    self.tokens = lexer.tokens\n+    self.source = source\n+    self.filename = filename\n+\n+  # Names of functions\n+  #\n+  # In general, we name functions after the left-hand-side of the rule(s) that\n+  # they handle. E.g., |p_foo_bar| for a rule |foo_bar : ...|.\n+  #\n+  # There may be multiple functions handling rules for the same left-hand-side;\n+  # then we name the functions |p_foo_bar_N| (for left-hand-side |foo_bar|),\n+  # where N is a number (numbered starting from 1). Note that using multiple\n+  # functions is actually more efficient than having single functions handle\n+  # multiple rules (and, e.g., distinguishing them by examining |len(p)|).\n+  #\n+  # It's also possible to have a function handling multiple rules with different\n+  # left-hand-sides. We do not do this.\n+  #\n+  # See http://www.dabeaz.com/ply/ply.html#ply_nn25 for more details.\n+\n+  # TODO(vtl): Get rid of the braces in the module \"statement\". (Consider\n+  # renaming \"module\" -> \"package\".) Then we'll be able to have a single rule\n+  # for root (by making module \"optional\").\n+  def p_root_1(self, p):\n+    \"\"\"root : \"\"\"\n+    p[0] = ast.Mojom(None, ast.ImportList(), [])\n+\n+  def p_root_2(self, p):\n+    \"\"\"root : root module\"\"\"\n+    if p[1].module is not None:\n+      raise ParseError(\n+          self.filename,\n+          \"Multiple \\\"module\\\" statements not allowed:\",\n+          p[2].lineno,\n+          snippet=self._GetSnippet(p[2].lineno))\n+    if p[1].import_list.items or p[1].definition_list:\n+      raise ParseError(\n+          self.filename,\n+          \"\\\"module\\\" statements must precede imports and definitions:\",\n+          p[2].lineno,\n+          snippet=self._GetSnippet(p[2].lineno))\n+    p[0] = p[1]\n+    p[0].module = p[2]\n+\n+  def p_root_3(self, p):\n+    \"\"\"root : root import\"\"\"\n+    if p[1].definition_list:\n+      raise ParseError(\n+          self.filename,\n+          \"\\\"import\\\" statements must precede definitions:\",\n+          p[2].lineno,\n+          snippet=self._GetSnippet(p[2].lineno))\n+    p[0] = p[1]\n+    p[0].import_list.Append(p[2])\n+\n+  def p_root_4(self, p):\n+    \"\"\"root : root definition\"\"\"\n+    p[0] = p[1]\n+    p[0].definition_list.append(p[2])\n+\n+  def p_import(self, p):\n+    \"\"\"import : attribute_section IMPORT STRING_LITERAL SEMI\"\"\"\n+    # 'eval' the literal to strip the quotes.\n+    # TODO(vtl): This eval is dubious. We should unquote/unescape ourselves.\n+    p[0] = ast.Import(\n+        p[1], eval(p[3]), filename=self.filename, lineno=p.lineno(2))\n+\n+  def p_module(self, p):\n+    \"\"\"module : attribute_section MODULE identifier_wrapped SEMI\"\"\"\n+    p[0] = ast.Module(p[3], p[1], filename=self.filename, lineno=p.lineno(2))\n+\n+  def p_definition(self, p):\n+    \"\"\"definition : struct\n+                  | union\n+                  | interface\n+                  | enum\n+                  | const\"\"\"\n+    p[0] = p[1]\n+\n+  def p_attribute_section_1(self, p):\n+    \"\"\"attribute_section : \"\"\"\n+    p[0] = None\n+\n+  def p_attribute_section_2(self, p):\n+    \"\"\"attribute_section : LBRACKET attribute_list RBRACKET\"\"\"\n+    p[0] = p[2]\n+\n+  def p_attribute_list_1(self, p):\n+    \"\"\"attribute_list : \"\"\"\n+    p[0] = ast.AttributeList()\n+\n+  def p_attribute_list_2(self, p):\n+    \"\"\"attribute_list : nonempty_attribute_list\"\"\"\n+    p[0] = p[1]\n+\n+  def p_nonempty_attribute_list_1(self, p):\n+    \"\"\"nonempty_attribute_list : attribute\"\"\"\n+    p[0] = ast.AttributeList(p[1])\n+\n+  def p_nonempty_attribute_list_2(self, p):\n+    \"\"\"nonempty_attribute_list : nonempty_attribute_list COMMA attribute\"\"\"\n+    p[0] = p[1]\n+    p[0].Append(p[3])\n+\n+  def p_attribute_1(self, p):\n+    \"\"\"attribute : NAME EQUALS evaled_literal\n+                 | NAME EQUALS NAME\"\"\"\n+    p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1))\n+\n+  def p_attribute_2(self, p):\n+    \"\"\"attribute : NAME\"\"\"\n+    p[0] = ast.Attribute(p[1], True, filename=self.filename, lineno=p.lineno(1))\n+\n+  def p_evaled_literal(self, p):\n+    \"\"\"evaled_literal : literal\"\"\"\n+    # 'eval' the literal to strip the quotes. Handle keywords \"true\" and \"false\"\n+    # specially since they cannot directly be evaluated to python boolean\n+    # values.\n+    if p[1] == \"true\":\n+      p[0] = True\n+    elif p[1] == \"false\":\n+      p[0] = False\n+    else:\n+      p[0] = eval(p[1])\n+\n+  def p_struct_1(self, p):\n+    \"\"\"struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI\"\"\"\n+    p[0] = ast.Struct(p[3], p[1], p[5])\n+\n+  def p_struct_2(self, p):\n+    \"\"\"struct : attribute_section STRUCT NAME SEMI\"\"\"\n+    p[0] = ast.Struct(p[3], p[1], None)\n+\n+  def p_struct_body_1(self, p):\n+    \"\"\"struct_body : \"\"\"\n+    p[0] = ast.StructBody()\n+\n+  def p_struct_body_2(self, p):\n+    \"\"\"struct_body : struct_body const\n+                   | struct_body enum\n+                   | struct_body struct_field\"\"\"\n+    p[0] = p[1]\n+    p[0].Append(p[2])\n+\n+  def p_struct_field(self, p):\n+    \"\"\"struct_field : attribute_section typename NAME ordinal default SEMI\"\"\"\n+    p[0] = ast.StructField(p[3], p[1], p[4], p[2], p[5])\n+\n+  def p_union(self, p):\n+    \"\"\"union : attribute_section UNION NAME LBRACE union_body RBRACE SEMI\"\"\"\n+    p[0] = ast.Union(p[3], p[1], p[5])\n+\n+  def p_union_body_1(self, p):\n+    \"\"\"union_body : \"\"\"\n+    p[0] = ast.UnionBody()\n+\n+  def p_union_body_2(self, p):\n+    \"\"\"union_body : union_body union_field\"\"\"\n+    p[0] = p[1]\n+    p[1].Append(p[2])\n+\n+  def p_union_field(self, p):\n+    \"\"\"union_field : attribute_section typename NAME ordinal SEMI\"\"\"\n+    p[0] = ast.UnionField(p[3], p[1], p[4], p[2])\n+\n+  def p_default_1(self, p):\n+    \"\"\"default : \"\"\"\n+    p[0] = None\n+\n+  def p_default_2(self, p):\n+    \"\"\"default : EQUALS constant\"\"\"\n+    p[0] = p[2]\n+\n+  def p_interface(self, p):\n+    \"\"\"interface : attribute_section INTERFACE NAME LBRACE interface_body \\\n+                       RBRACE SEMI\"\"\"\n+    p[0] = ast.Interface(p[3], p[1], p[5])\n+\n+  def p_interface_body_1(self, p):\n+    \"\"\"interface_body : \"\"\"\n+    p[0] = ast.InterfaceBody()\n+\n+  def p_interface_body_2(self, p):\n+    \"\"\"interface_body : interface_body const\n+                      | interface_body enum\n+                      | interface_body method\"\"\"\n+    p[0] = p[1]\n+    p[0].Append(p[2])\n+\n+  def p_response_1(self, p):\n+    \"\"\"response : \"\"\"\n+    p[0] = None\n+\n+  def p_response_2(self, p):\n+    \"\"\"response : RESPONSE LPAREN parameter_list RPAREN\"\"\"\n+    p[0] = p[3]\n+\n+  def p_method(self, p):\n+    \"\"\"method : attribute_section NAME ordinal LPAREN parameter_list RPAREN \\\n+                    response SEMI\"\"\"\n+    p[0] = ast.Method(p[2], p[1], p[3], p[5], p[7])\n+\n+  def p_parameter_list_1(self, p):\n+    \"\"\"parameter_list : \"\"\"\n+    p[0] = ast.ParameterList()\n+\n+  def p_parameter_list_2(self, p):\n+    \"\"\"parameter_list : nonempty_parameter_list\"\"\"\n+    p[0] = p[1]\n+\n+  def p_nonempty_parameter_list_1(self, p):\n+    \"\"\"nonempty_parameter_list : parameter\"\"\"\n+    p[0] = ast.ParameterList(p[1])\n+\n+  def p_nonempty_parameter_list_2(self, p):\n+    \"\"\"nonempty_parameter_list : nonempty_parameter_list COMMA parameter\"\"\"\n+    p[0] = p[1]\n+    p[0].Append(p[3])\n+\n+  def p_parameter(self, p):\n+    \"\"\"parameter : attribute_section typename NAME ordinal\"\"\"\n+    p[0] = ast.Parameter(\n+        p[3], p[1], p[4], p[2], filename=self.filename, lineno=p.lineno(3))\n+\n+  def p_typename(self, p):\n+    \"\"\"typename : nonnullable_typename QSTN\n+                | nonnullable_typename\"\"\"\n+    if len(p) == 2:\n+      p[0] = p[1]\n+    else:\n+      p[0] = p[1] + \"?\"\n+\n+  def p_nonnullable_typename(self, p):\n+    \"\"\"nonnullable_typename : basictypename\n+                            | array\n+                            | fixed_array\n+                            | associative_array\n+                            | interfacerequest\"\"\"\n+    p[0] = p[1]\n+\n+  def p_basictypename(self, p):\n+    \"\"\"basictypename : remotetype\n+                     | receivertype\n+                     | associatedremotetype\n+                     | associatedreceivertype\n+                     | identifier\n+                     | ASSOCIATED identifier\n+                     | handletype\"\"\"\n+    if len(p) == 2:\n+      p[0] = p[1]\n+    else:\n+      p[0] = \"asso<\" + p[2] + \">\"\n+\n+  def p_remotetype(self, p):\n+    \"\"\"remotetype : PENDING_REMOTE LANGLE identifier RANGLE\"\"\"\n+    p[0] = \"rmt<%s>\" % p[3]\n+\n+  def p_receivertype(self, p):\n+    \"\"\"receivertype : PENDING_RECEIVER LANGLE identifier RANGLE\"\"\"\n+    p[0] = \"rcv<%s>\" % p[3]\n+\n+  def p_associatedremotetype(self, p):\n+    \"\"\"associatedremotetype : PENDING_ASSOCIATED_REMOTE LANGLE identifier \\\n+                                  RANGLE\"\"\"\n+    p[0] = \"rma<%s>\" % p[3]\n+\n+  def p_associatedreceivertype(self, p):\n+    \"\"\"associatedreceivertype : PENDING_ASSOCIATED_RECEIVER LANGLE identifier \\\n+                                    RANGLE\"\"\"\n+    p[0] = \"rca<%s>\" % p[3]\n+\n+  def p_handletype(self, p):\n+    \"\"\"handletype : HANDLE\n+                  | HANDLE LANGLE NAME RANGLE\"\"\"\n+    if len(p) == 2:\n+      p[0] = p[1]\n+    else:\n+      if p[3] not in ('data_pipe_consumer', 'data_pipe_producer',\n+                      'message_pipe', 'shared_buffer', 'platform'):\n+        # Note: We don't enable tracking of line numbers for everything, so we\n+        # can't use |p.lineno(3)|.\n+        raise ParseError(\n+            self.filename,\n+            \"Invalid handle type %r:\" % p[3],\n+            lineno=p.lineno(1),\n+            snippet=self._GetSnippet(p.lineno(1)))\n+      p[0] = \"handle<\" + p[3] + \">\"\n+\n+  def p_array(self, p):\n+    \"\"\"array : ARRAY LANGLE typename RANGLE\"\"\"\n+    p[0] = p[3] + \"[]\"\n+\n+  def p_fixed_array(self, p):\n+    \"\"\"fixed_array : ARRAY LANGLE typename COMMA INT_CONST_DEC RANGLE\"\"\"\n+    value = int(p[5])\n+    if value == 0 or value > _MAX_ARRAY_SIZE:\n+      raise ParseError(\n+          self.filename,\n+          \"Fixed array size %d invalid:\" % value,\n+          lineno=p.lineno(5),\n+          snippet=self._GetSnippet(p.lineno(5)))\n+    p[0] = p[3] + \"[\" + p[5] + \"]\"\n+\n+  def p_associative_array(self, p):\n+    \"\"\"associative_array : MAP LANGLE identifier COMMA typename RANGLE\"\"\"\n+    p[0] = p[5] + \"{\" + p[3] + \"}\"\n+\n+  def p_interfacerequest(self, p):\n+    \"\"\"interfacerequest : identifier AMP\n+                        | ASSOCIATED identifier AMP\"\"\"\n+    if len(p) == 3:\n+      p[0] = p[1] + \"&\"\n+    else:\n+      p[0] = \"asso<\" + p[2] + \"&>\"\n+\n+  def p_ordinal_1(self, p):\n+    \"\"\"ordinal : \"\"\"\n+    p[0] = None\n+\n+  def p_ordinal_2(self, p):\n+    \"\"\"ordinal : ORDINAL\"\"\"\n+    value = int(p[1][1:])\n+    if value > _MAX_ORDINAL_VALUE:\n+      raise ParseError(\n+          self.filename,\n+          \"Ordinal value %d too large:\" % value,\n+          lineno=p.lineno(1),\n+          snippet=self._GetSnippet(p.lineno(1)))\n+    p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))\n+\n+  def p_enum_1(self, p):\n+    \"\"\"enum : attribute_section ENUM NAME LBRACE enum_value_list \\\n+                  RBRACE SEMI\n+            | attribute_section ENUM NAME LBRACE nonempty_enum_value_list \\\n+                  COMMA RBRACE SEMI\"\"\"\n+    p[0] = ast.Enum(\n+        p[3], p[1], p[5], filename=self.filename, lineno=p.lineno(2))\n+\n+  def p_enum_2(self, p):\n+    \"\"\"enum : attribute_section ENUM NAME SEMI\"\"\"\n+    p[0] = ast.Enum(\n+        p[3], p[1], None, filename=self.filename, lineno=p.lineno(2))\n+\n+  def p_enum_value_list_1(self, p):\n+    \"\"\"enum_value_list : \"\"\"\n+    p[0] = ast.EnumValueList()\n+\n+  def p_enum_value_list_2(self, p):\n+    \"\"\"enum_value_list : nonempty_enum_value_list\"\"\"\n+    p[0] = p[1]\n+\n+  def p_nonempty_enum_value_list_1(self, p):\n+    \"\"\"nonempty_enum_value_list : enum_value\"\"\"\n+    p[0] = ast.EnumValueList(p[1])\n+\n+  def p_nonempty_enum_value_list_2(self, p):\n+    \"\"\"nonempty_enum_value_list : nonempty_enum_value_list COMMA enum_value\"\"\"\n+    p[0] = p[1]\n+    p[0].Append(p[3])\n+\n+  def p_enum_value(self, p):\n+    \"\"\"enum_value : attribute_section NAME\n+                  | attribute_section NAME EQUALS int\n+                  | attribute_section NAME EQUALS identifier_wrapped\"\"\"\n+    p[0] = ast.EnumValue(\n+        p[2],\n+        p[1],\n+        p[4] if len(p) == 5 else None,\n+        filename=self.filename,\n+        lineno=p.lineno(2))\n+\n+  def p_const(self, p):\n+    \"\"\"const : attribute_section CONST typename NAME EQUALS constant SEMI\"\"\"\n+    p[0] = ast.Const(p[4], p[1], p[3], p[6])\n+\n+  def p_constant(self, p):\n+    \"\"\"constant : literal\n+                | identifier_wrapped\"\"\"\n+    p[0] = p[1]\n+\n+  def p_identifier_wrapped(self, p):\n+    \"\"\"identifier_wrapped : identifier\"\"\"\n+    p[0] = ('IDENTIFIER', p[1])\n+\n+  # TODO(vtl): Make this produce a \"wrapped\" identifier (probably as an\n+  # |ast.Identifier|, to be added) and get rid of identifier_wrapped.\n+  def p_identifier(self, p):\n+    \"\"\"identifier : NAME\n+                  | NAME DOT identifier\"\"\"\n+    p[0] = ''.join(p[1:])\n+\n+  def p_literal(self, p):\n+    \"\"\"literal : int\n+               | float\n+               | TRUE\n+               | FALSE\n+               | DEFAULT\n+               | STRING_LITERAL\"\"\"\n+    p[0] = p[1]\n+\n+  def p_int(self, p):\n+    \"\"\"int : int_const\n+           | PLUS int_const\n+           | MINUS int_const\"\"\"\n+    p[0] = ''.join(p[1:])\n+\n+  def p_int_const(self, p):\n+    \"\"\"int_const : INT_CONST_DEC\n+                 | INT_CONST_HEX\"\"\"\n+    p[0] = p[1]\n+\n+  def p_float(self, p):\n+    \"\"\"float : FLOAT_CONST\n+             | PLUS FLOAT_CONST\n+             | MINUS FLOAT_CONST\"\"\"\n+    p[0] = ''.join(p[1:])\n+\n+  def p_error(self, e):\n+    if e is None:\n+      # Unexpected EOF.\n+      # TODO(vtl): Can we figure out what's missing?\n+      raise ParseError(self.filename, \"Unexpected end of file\")\n+\n+    raise ParseError(\n+        self.filename,\n+        \"Unexpected %r:\" % e.value,\n+        lineno=e.lineno,\n+        snippet=self._GetSnippet(e.lineno))\n+\n+  def _GetSnippet(self, lineno):\n+    return self.source.split('\\n')[lineno - 1]\n+\n+\n+def Parse(source, filename):\n+  \"\"\"Parse source file to AST.\n+\n+  Args:\n+    source: The source text as a str (Python 2 or 3) or unicode (Python 2).\n+    filename: The filename that |source| originates from.\n+\n+  Returns:\n+    The AST as a mojom.parse.ast.Mojom object.\n+  \"\"\"\n+  lexer = Lexer(filename)\n+  parser = Parser(lexer, source, filename)\n+\n+  lex.lex(object=lexer)\n+  yacc.yacc(module=parser, debug=0, write_tables=0)\n+\n+  tree = yacc.parse(source)\n+  return tree\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py\nnew file mode 100644\nindex 00000000..6d6b7153\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py\n@@ -0,0 +1,1390 @@\n+# Copyright 2014 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import imp\n+import os.path\n+import sys\n+import unittest\n+\n+from mojom.parse import ast\n+from mojom.parse import lexer\n+from mojom.parse import parser\n+\n+\n+class ParserTest(unittest.TestCase):\n+  \"\"\"Tests |parser.Parse()|.\"\"\"\n+\n+  def testTrivialValidSource(self):\n+    \"\"\"Tests a trivial, but valid, .mojom source.\"\"\"\n+\n+    source = \"\"\"\\\n+        // This is a comment.\n+\n+        module my_module;\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testSourceWithCrLfs(self):\n+    \"\"\"Tests a .mojom source with CR-LFs instead of LFs.\"\"\"\n+\n+    source = \"// This is a comment.\\r\\n\\r\\nmodule my_module;\\r\\n\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testUnexpectedEOF(self):\n+    \"\"\"Tests a \"truncated\" .mojom source.\"\"\"\n+\n+    source = \"\"\"\\\n+        // This is a comment.\n+\n+        module my_module\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom: Error: Unexpected end of file$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testCommentLineNumbers(self):\n+    \"\"\"Tests that line numbers are correctly tracked when comments are\n+    present.\"\"\"\n+\n+    source1 = \"\"\"\\\n+        // Isolated C++-style comments.\n+\n+        // Foo.\n+        asdf1\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:4: Error: Unexpected 'asdf1':\\n *asdf1$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    source2 = \"\"\"\\\n+        // Consecutive C++-style comments.\n+        // Foo.\n+        // Bar.\n+\n+        struct Yada {  // Baz.\n+                       // Quux.\n+          int32 x;\n+        };\n+\n+        asdf2\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:10: Error: Unexpected 'asdf2':\\n *asdf2$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    source3 = \"\"\"\\\n+        /* Single-line C-style comments. */\n+        /* Foobar. */\n+\n+        /* Baz. */\n+        asdf3\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:5: Error: Unexpected 'asdf3':\\n *asdf3$\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+    source4 = \"\"\"\\\n+        /* Multi-line C-style comments.\n+        */\n+        /*\n+        Foo.\n+        Bar.\n+        */\n+\n+        /* Baz\n+           Quux. */\n+        asdf4\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:10: Error: Unexpected 'asdf4':\\n *asdf4$\"):\n+      parser.Parse(source4, \"my_file.mojom\")\n+\n+  def testSimpleStruct(self):\n+    \"\"\"Tests a simple .mojom source that just defines a struct.\"\"\"\n+\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        struct MyStruct {\n+          int32 a;\n+          double b;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Struct(\n+                'MyStruct', None,\n+                ast.StructBody([\n+                    ast.StructField('a', None, None, 'int32', None),\n+                    ast.StructField('b', None, None, 'double', None)\n+                ]))\n+        ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testSimpleStructWithoutModule(self):\n+    \"\"\"Tests a simple struct without an explict module statement.\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          int32 a;\n+          double b;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody([\n+                ast.StructField('a', None, None, 'int32', None),\n+                ast.StructField('b', None, None, 'double', None)\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testValidStructDefinitions(self):\n+    \"\"\"Tests all types of definitions that can occur in a struct.\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          enum MyEnum { VALUE };\n+          const double kMyConst = 1.23;\n+          int32 a;\n+          SomeOtherStruct b;  // Invalidity detected at another stage.\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody([\n+                ast.Enum('MyEnum', None,\n+                         ast.EnumValueList(ast.EnumValue('VALUE', None, None))),\n+                ast.Const('kMyConst', None, 'double', '1.23'),\n+                ast.StructField('a', None, None, 'int32', None),\n+                ast.StructField('b', None, None, 'SomeOtherStruct', None)\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testInvalidStructDefinitions(self):\n+    \"\"\"Tests that definitions that aren't allowed in a struct are correctly\n+    detected.\"\"\"\n+\n+    source1 = \"\"\"\\\n+        struct MyStruct {\n+          MyMethod(int32 a);\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected '\\(':\\n\"\n+        r\" *MyMethod\\(int32 a\\);$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    source2 = \"\"\"\\\n+        struct MyStruct {\n+          struct MyInnerStruct {\n+            int32 a;\n+          };\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected 'struct':\\n\"\n+        r\" *struct MyInnerStruct {$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    source3 = \"\"\"\\\n+        struct MyStruct {\n+          interface MyInterface {\n+            MyMethod(int32 a);\n+          };\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:2: Error: Unexpected 'interface':\\n\"\n+        r\" *interface MyInterface {$\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+  def testMissingModuleName(self):\n+    \"\"\"Tests an (invalid) .mojom with a missing module name.\"\"\"\n+\n+    source1 = \"\"\"\\\n+        // Missing module name.\n+        module ;\n+        struct MyStruct {\n+          int32 a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:2: Error: Unexpected ';':\\n *module ;$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    # Another similar case, but make sure that line-number tracking/reporting\n+    # is correct.\n+    source2 = \"\"\"\\\n+        module\n+        // This line intentionally left unblank.\n+\n+        struct MyStruct {\n+          int32 a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:4: Error: Unexpected 'struct':\\n\"\n+        r\" *struct MyStruct {$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+  def testMultipleModuleStatements(self):\n+    \"\"\"Tests an (invalid) .mojom with multiple module statements.\"\"\"\n+\n+    source = \"\"\"\\\n+        module foo;\n+        module bar;\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:2: Error: Multiple \\\"module\\\" statements not \"\n+        r\"allowed:\\n *module bar;$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testModuleStatementAfterImport(self):\n+    \"\"\"Tests an (invalid) .mojom with a module statement after an import.\"\"\"\n+\n+    source = \"\"\"\\\n+        import \"foo.mojom\";\n+        module foo;\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:2: Error: \\\"module\\\" statements must precede imports \"\n+        r\"and definitions:\\n *module foo;$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testModuleStatementAfterDefinition(self):\n+    \"\"\"Tests an (invalid) .mojom with a module statement after a definition.\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          int32 a;\n+        };\n+        module foo;\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:4: Error: \\\"module\\\" statements must precede imports \"\n+        r\"and definitions:\\n *module foo;$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testImportStatementAfterDefinition(self):\n+    \"\"\"Tests an (invalid) .mojom with an import statement after a definition.\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          int32 a;\n+        };\n+        import \"foo.mojom\";\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:4: Error: \\\"import\\\" statements must precede \"\n+        r\"definitions:\\n *import \\\"foo.mojom\\\";$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testEnums(self):\n+    \"\"\"Tests that enum statements are correctly parsed.\"\"\"\n+\n+    source = \"\"\"\\\n+        module my_module;\n+        enum MyEnum1 { VALUE1, VALUE2 };  // No trailing comma.\n+        enum MyEnum2 {\n+          VALUE1 = -1,\n+          VALUE2 = 0,\n+          VALUE3 = + 987,  // Check that space is allowed.\n+          VALUE4 = 0xAF12,\n+          VALUE5 = -0x09bcd,\n+          VALUE6 = VALUE5,\n+          VALUE7,  // Leave trailing comma.\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Enum(\n+                'MyEnum1', None,\n+                ast.EnumValueList([\n+                    ast.EnumValue('VALUE1', None, None),\n+                    ast.EnumValue('VALUE2', None, None)\n+                ])),\n+            ast.Enum(\n+                'MyEnum2', None,\n+                ast.EnumValueList([\n+                    ast.EnumValue('VALUE1', None, '-1'),\n+                    ast.EnumValue('VALUE2', None, '0'),\n+                    ast.EnumValue('VALUE3', None, '+987'),\n+                    ast.EnumValue('VALUE4', None, '0xAF12'),\n+                    ast.EnumValue('VALUE5', None, '-0x09bcd'),\n+                    ast.EnumValue('VALUE6', None, ('IDENTIFIER', 'VALUE5')),\n+                    ast.EnumValue('VALUE7', None, None)\n+                ]))\n+        ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testInvalidEnumInitializers(self):\n+    \"\"\"Tests that invalid enum initializers are correctly detected.\"\"\"\n+\n+    # Floating point value.\n+    source2 = \"enum MyEnum { VALUE = 0.123 };\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:1: Error: Unexpected '0\\.123':\\n\"\n+        r\"enum MyEnum { VALUE = 0\\.123 };$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    # Boolean value.\n+    source2 = \"enum MyEnum { VALUE = true };\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:1: Error: Unexpected 'true':\\n\"\n+        r\"enum MyEnum { VALUE = true };$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+  def testConsts(self):\n+    \"\"\"Tests some constants and struct members initialized with them.\"\"\"\n+\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        struct MyStruct {\n+          const int8 kNumber = -1;\n+          int8 number@0 = kNumber;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Struct(\n+                'MyStruct', None,\n+                ast.StructBody([\n+                    ast.Const('kNumber', None, 'int8', '-1'),\n+                    ast.StructField('number', None, ast.Ordinal(0), 'int8',\n+                                    ('IDENTIFIER', 'kNumber'))\n+                ]))\n+        ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testNoConditionals(self):\n+    \"\"\"Tests that ?: is not allowed.\"\"\"\n+\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        enum MyEnum {\n+          MY_ENUM_1 = 1 ? 2 : 3\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:4: Error: Unexpected '\\?':\\n\"\n+        r\" *MY_ENUM_1 = 1 \\? 2 : 3$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testSimpleOrdinals(self):\n+    \"\"\"Tests that (valid) ordinal values are scanned correctly.\"\"\"\n+\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        // This isn't actually valid .mojom, but the problem (missing ordinals)\n+        // should be handled at a different level.\n+        struct MyStruct {\n+          int32 a0@0;\n+          int32 a1@1;\n+          int32 a2@2;\n+          int32 a9@9;\n+          int32 a10 @10;\n+          int32 a11 @11;\n+          int32 a29 @29;\n+          int32 a1234567890 @1234567890;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Struct(\n+                'MyStruct', None,\n+                ast.StructBody([\n+                    ast.StructField('a0', None, ast.Ordinal(0), 'int32', None),\n+                    ast.StructField('a1', None, ast.Ordinal(1), 'int32', None),\n+                    ast.StructField('a2', None, ast.Ordinal(2), 'int32', None),\n+                    ast.StructField('a9', None, ast.Ordinal(9), 'int32', None),\n+                    ast.StructField('a10', None, ast.Ordinal(10), 'int32',\n+                                    None),\n+                    ast.StructField('a11', None, ast.Ordinal(11), 'int32',\n+                                    None),\n+                    ast.StructField('a29', None, ast.Ordinal(29), 'int32',\n+                                    None),\n+                    ast.StructField('a1234567890', None,\n+                                    ast.Ordinal(1234567890), 'int32', None)\n+                ]))\n+        ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testInvalidOrdinals(self):\n+    \"\"\"Tests that (lexically) invalid ordinals are correctly detected.\"\"\"\n+\n+    source1 = \"\"\"\\\n+        module my_module;\n+\n+        struct MyStruct {\n+          int32 a_missing@;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        lexer.LexError, r\"^my_file\\.mojom:4: Error: Missing ordinal value$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    source2 = \"\"\"\\\n+        module my_module;\n+\n+        struct MyStruct {\n+          int32 a_octal@01;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        lexer.LexError, r\"^my_file\\.mojom:4: Error: \"\n+        r\"Octal and hexadecimal ordinal values not allowed$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    source3 = \"\"\"\\\n+        module my_module; struct MyStruct { int32 a_invalid_octal@08; };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        lexer.LexError, r\"^my_file\\.mojom:1: Error: \"\n+        r\"Octal and hexadecimal ordinal values not allowed$\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+    source4 = \"module my_module; struct MyStruct { int32 a_hex@0x1aB9; };\"\n+    with self.assertRaisesRegexp(\n+        lexer.LexError, r\"^my_file\\.mojom:1: Error: \"\n+        r\"Octal and hexadecimal ordinal values not allowed$\"):\n+      parser.Parse(source4, \"my_file.mojom\")\n+\n+    source5 = \"module my_module; struct MyStruct { int32 a_hex@0X0; };\"\n+    with self.assertRaisesRegexp(\n+        lexer.LexError, r\"^my_file\\.mojom:1: Error: \"\n+        r\"Octal and hexadecimal ordinal values not allowed$\"):\n+      parser.Parse(source5, \"my_file.mojom\")\n+\n+    source6 = \"\"\"\\\n+        struct MyStruct {\n+          int32 a_too_big@999999999999;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: \"\n+        r\"Ordinal value 999999999999 too large:\\n\"\n+        r\" *int32 a_too_big@999999999999;$\"):\n+      parser.Parse(source6, \"my_file.mojom\")\n+\n+  def testNestedNamespace(self):\n+    \"\"\"Tests that \"nested\" namespaces work.\"\"\"\n+\n+    source = \"\"\"\\\n+        module my.mod;\n+\n+        struct MyStruct {\n+          int32 a;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my.mod'), None), ast.ImportList(), [\n+            ast.Struct(\n+                'MyStruct', None,\n+                ast.StructBody(ast.StructField('a', None, None, 'int32', None)))\n+        ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testValidHandleTypes(self):\n+    \"\"\"Tests (valid) handle types.\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          handle a;\n+          handle<data_pipe_consumer> b;\n+          handle <data_pipe_producer> c;\n+          handle < message_pipe > d;\n+          handle\n+            < shared_buffer\n+            > e;\n+          handle\n+            <platform\n+\n+            > f;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody([\n+                ast.StructField('a', None, None, 'handle', None),\n+                ast.StructField('b', None, None, 'handle<data_pipe_consumer>',\n+                                None),\n+                ast.StructField('c', None, None, 'handle<data_pipe_producer>',\n+                                None),\n+                ast.StructField('d', None, None, 'handle<message_pipe>', None),\n+                ast.StructField('e', None, None, 'handle<shared_buffer>', None),\n+                ast.StructField('f', None, None, 'handle<platform>', None)\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testInvalidHandleType(self):\n+    \"\"\"Tests an invalid (unknown) handle type.\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          handle<wtf_is_this> foo;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: \"\n+        r\"Invalid handle type 'wtf_is_this':\\n\"\n+        r\" *handle<wtf_is_this> foo;$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testValidDefaultValues(self):\n+    \"\"\"Tests default values that are valid (to the parser).\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          int16 a0 = 0;\n+          uint16 a1 = 0x0;\n+          uint16 a2 = 0x00;\n+          uint16 a3 = 0x01;\n+          uint16 a4 = 0xcd;\n+          int32 a5 = 12345;\n+          int64 a6 = -12345;\n+          int64 a7 = +12345;\n+          uint32 a8 = 0x12cd3;\n+          uint32 a9 = -0x12cD3;\n+          uint32 a10 = +0x12CD3;\n+          bool a11 = true;\n+          bool a12 = false;\n+          float a13 = 1.2345;\n+          float a14 = -1.2345;\n+          float a15 = +1.2345;\n+          float a16 = 123.;\n+          float a17 = .123;\n+          double a18 = 1.23E10;\n+          double a19 = 1.E-10;\n+          double a20 = .5E+10;\n+          double a21 = -1.23E10;\n+          double a22 = +.123E10;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody([\n+                ast.StructField('a0', None, None, 'int16', '0'),\n+                ast.StructField('a1', None, None, 'uint16', '0x0'),\n+                ast.StructField('a2', None, None, 'uint16', '0x00'),\n+                ast.StructField('a3', None, None, 'uint16', '0x01'),\n+                ast.StructField('a4', None, None, 'uint16', '0xcd'),\n+                ast.StructField('a5', None, None, 'int32', '12345'),\n+                ast.StructField('a6', None, None, 'int64', '-12345'),\n+                ast.StructField('a7', None, None, 'int64', '+12345'),\n+                ast.StructField('a8', None, None, 'uint32', '0x12cd3'),\n+                ast.StructField('a9', None, None, 'uint32', '-0x12cD3'),\n+                ast.StructField('a10', None, None, 'uint32', '+0x12CD3'),\n+                ast.StructField('a11', None, None, 'bool', 'true'),\n+                ast.StructField('a12', None, None, 'bool', 'false'),\n+                ast.StructField('a13', None, None, 'float', '1.2345'),\n+                ast.StructField('a14', None, None, 'float', '-1.2345'),\n+                ast.StructField('a15', None, None, 'float', '+1.2345'),\n+                ast.StructField('a16', None, None, 'float', '123.'),\n+                ast.StructField('a17', None, None, 'float', '.123'),\n+                ast.StructField('a18', None, None, 'double', '1.23E10'),\n+                ast.StructField('a19', None, None, 'double', '1.E-10'),\n+                ast.StructField('a20', None, None, 'double', '.5E+10'),\n+                ast.StructField('a21', None, None, 'double', '-1.23E10'),\n+                ast.StructField('a22', None, None, 'double', '+.123E10')\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testValidFixedSizeArray(self):\n+    \"\"\"Tests parsing a fixed size array.\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          array<int32> normal_array;\n+          array<int32, 1> fixed_size_array_one_entry;\n+          array<int32, 10> fixed_size_array_ten_entries;\n+          array<array<array<int32, 1>>, 2> nested_arrays;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody([\n+                ast.StructField('normal_array', None, None, 'int32[]', None),\n+                ast.StructField('fixed_size_array_one_entry', None, None,\n+                                'int32[1]', None),\n+                ast.StructField('fixed_size_array_ten_entries', None, None,\n+                                'int32[10]', None),\n+                ast.StructField('nested_arrays', None, None, 'int32[1][][2]',\n+                                None)\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testValidNestedArray(self):\n+    \"\"\"Tests parsing a nested array.\"\"\"\n+\n+    source = \"struct MyStruct { array<array<int32>> nested_array; };\"\n+    expected = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody(\n+                ast.StructField('nested_array', None, None, 'int32[][]', None)))\n+    ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testInvalidFixedArraySize(self):\n+    \"\"\"Tests that invalid fixed array bounds are correctly detected.\"\"\"\n+\n+    source1 = \"\"\"\\\n+        struct MyStruct {\n+          array<int32, 0> zero_size_array;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:2: Error: Fixed array size 0 invalid:\\n\"\n+        r\" *array<int32, 0> zero_size_array;$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    source2 = \"\"\"\\\n+        struct MyStruct {\n+          array<int32, 999999999999> too_big_array;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:2: Error: Fixed array size 999999999999 invalid:\\n\"\n+        r\" *array<int32, 999999999999> too_big_array;$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    source3 = \"\"\"\\\n+        struct MyStruct {\n+          array<int32, abcdefg> not_a_number;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected 'abcdefg':\\n\"\n+        r\" *array<int32, abcdefg> not_a_number;\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+  def testValidAssociativeArrays(self):\n+    \"\"\"Tests that we can parse valid associative array structures.\"\"\"\n+\n+    source1 = \"struct MyStruct { map<string, uint8> data; };\"\n+    expected1 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody(\n+                [ast.StructField('data', None, None, 'uint8{string}', None)]))\n+    ])\n+    self.assertEquals(parser.Parse(source1, \"my_file.mojom\"), expected1)\n+\n+    source2 = \"interface MyInterface { MyMethod(map<string, uint8> a); };\"\n+    expected2 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Interface(\n+            'MyInterface', None,\n+            ast.InterfaceBody(\n+                ast.Method(\n+                    'MyMethod', None, None,\n+                    ast.ParameterList(\n+                        ast.Parameter('a', None, None, 'uint8{string}')),\n+                    None)))\n+    ])\n+    self.assertEquals(parser.Parse(source2, \"my_file.mojom\"), expected2)\n+\n+    source3 = \"struct MyStruct { map<string, array<uint8>> data; };\"\n+    expected3 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody(\n+                [ast.StructField('data', None, None, 'uint8[]{string}', None)]))\n+    ])\n+    self.assertEquals(parser.Parse(source3, \"my_file.mojom\"), expected3)\n+\n+  def testValidMethod(self):\n+    \"\"\"Tests parsing method declarations.\"\"\"\n+\n+    source1 = \"interface MyInterface { MyMethod(int32 a); };\"\n+    expected1 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Interface(\n+            'MyInterface', None,\n+            ast.InterfaceBody(\n+                ast.Method(\n+                    'MyMethod', None, None,\n+                    ast.ParameterList(ast.Parameter('a', None, None, 'int32')),\n+                    None)))\n+    ])\n+    self.assertEquals(parser.Parse(source1, \"my_file.mojom\"), expected1)\n+\n+    source2 = \"\"\"\\\n+        interface MyInterface {\n+          MyMethod1@0(int32 a@0, int64 b@1);\n+          MyMethod2@1() => ();\n+        };\n+        \"\"\"\n+    expected2 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Interface(\n+            'MyInterface', None,\n+            ast.InterfaceBody([\n+                ast.Method(\n+                    'MyMethod1', None, ast.Ordinal(0),\n+                    ast.ParameterList([\n+                        ast.Parameter('a', None, ast.Ordinal(0), 'int32'),\n+                        ast.Parameter('b', None, ast.Ordinal(1), 'int64')\n+                    ]), None),\n+                ast.Method('MyMethod2', None, ast.Ordinal(1),\n+                           ast.ParameterList(), ast.ParameterList())\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source2, \"my_file.mojom\"), expected2)\n+\n+    source3 = \"\"\"\\\n+        interface MyInterface {\n+          MyMethod(string a) => (int32 a, bool b);\n+        };\n+        \"\"\"\n+    expected3 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Interface(\n+            'MyInterface', None,\n+            ast.InterfaceBody(\n+                ast.Method(\n+                    'MyMethod', None, None,\n+                    ast.ParameterList(ast.Parameter('a', None, None, 'string')),\n+                    ast.ParameterList([\n+                        ast.Parameter('a', None, None, 'int32'),\n+                        ast.Parameter('b', None, None, 'bool')\n+                    ]))))\n+    ])\n+    self.assertEquals(parser.Parse(source3, \"my_file.mojom\"), expected3)\n+\n+  def testInvalidMethods(self):\n+    \"\"\"Tests that invalid method declarations are correctly detected.\"\"\"\n+\n+    # No trailing commas.\n+    source1 = \"\"\"\\\n+        interface MyInterface {\n+          MyMethod(string a,);\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected '\\)':\\n\"\n+        r\" *MyMethod\\(string a,\\);$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    # No leading commas.\n+    source2 = \"\"\"\\\n+        interface MyInterface {\n+          MyMethod(, string a);\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected ',':\\n\"\n+        r\" *MyMethod\\(, string a\\);$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+  def testValidInterfaceDefinitions(self):\n+    \"\"\"Tests all types of definitions that can occur in an interface.\"\"\"\n+\n+    source = \"\"\"\\\n+        interface MyInterface {\n+          enum MyEnum { VALUE };\n+          const int32 kMyConst = 123;\n+          MyMethod(int32 x) => (MyEnum y);\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(None, ast.ImportList(), [\n+        ast.Interface(\n+            'MyInterface', None,\n+            ast.InterfaceBody([\n+                ast.Enum('MyEnum', None,\n+                         ast.EnumValueList(ast.EnumValue('VALUE', None, None))),\n+                ast.Const('kMyConst', None, 'int32', '123'),\n+                ast.Method(\n+                    'MyMethod', None, None,\n+                    ast.ParameterList(ast.Parameter('x', None, None, 'int32')),\n+                    ast.ParameterList(ast.Parameter('y', None, None, 'MyEnum')))\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testInvalidInterfaceDefinitions(self):\n+    \"\"\"Tests that definitions that aren't allowed in an interface are correctly\n+    detected.\"\"\"\n+\n+    source1 = \"\"\"\\\n+        interface MyInterface {\n+          struct MyStruct {\n+            int32 a;\n+          };\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected 'struct':\\n\"\n+        r\" *struct MyStruct {$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    source2 = \"\"\"\\\n+        interface MyInterface {\n+          interface MyInnerInterface {\n+            MyMethod(int32 x);\n+          };\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:2: Error: Unexpected 'interface':\\n\"\n+        r\" *interface MyInnerInterface {$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    source3 = \"\"\"\\\n+        interface MyInterface {\n+          int32 my_field;\n+        };\n+        \"\"\"\n+    # The parser thinks that \"int32\" is a plausible name for a method, so it's\n+    # \"my_field\" that gives it away.\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected 'my_field':\\n\"\n+        r\" *int32 my_field;$\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+  def testValidAttributes(self):\n+    \"\"\"Tests parsing attributes (and attribute lists).\"\"\"\n+\n+    # Note: We use structs because they have (optional) attribute lists.\n+\n+    # Empty attribute list.\n+    source1 = \"[] struct MyStruct {};\"\n+    expected1 = ast.Mojom(\n+        None, ast.ImportList(),\n+        [ast.Struct('MyStruct', ast.AttributeList(), ast.StructBody())])\n+    self.assertEquals(parser.Parse(source1, \"my_file.mojom\"), expected1)\n+\n+    # One-element attribute list, with name value.\n+    source2 = \"[MyAttribute=MyName] struct MyStruct {};\"\n+    expected2 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct('MyStruct',\n+                   ast.AttributeList(ast.Attribute(\"MyAttribute\", \"MyName\")),\n+                   ast.StructBody())\n+    ])\n+    self.assertEquals(parser.Parse(source2, \"my_file.mojom\"), expected2)\n+\n+    # Two-element attribute list, with one string value and one integer value.\n+    source3 = \"[MyAttribute1 = \\\"hello\\\", MyAttribute2 = 5] struct MyStruct {};\"\n+    expected3 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct',\n+            ast.AttributeList([\n+                ast.Attribute(\"MyAttribute1\", \"hello\"),\n+                ast.Attribute(\"MyAttribute2\", 5)\n+            ]), ast.StructBody())\n+    ])\n+    self.assertEquals(parser.Parse(source3, \"my_file.mojom\"), expected3)\n+\n+    # Various places that attribute list is allowed.\n+    source4 = \"\"\"\\\n+        [Attr0=0] module my_module;\n+\n+        [Attr1=1] import \"my_import\";\n+\n+        [Attr2=2] struct MyStruct {\n+          [Attr3=3] int32 a;\n+        };\n+        [Attr4=4] union MyUnion {\n+          [Attr5=5] int32 a;\n+        };\n+        [Attr6=6] enum MyEnum {\n+          [Attr7=7] a\n+        };\n+        [Attr8=8] interface MyInterface {\n+          [Attr9=9] MyMethod([Attr10=10] int32 a) => ([Attr11=11] bool b);\n+        };\n+        [Attr12=12] const double kMyConst = 1.23;\n+        \"\"\"\n+    expected4 = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'),\n+                   ast.AttributeList([ast.Attribute(\"Attr0\", 0)])),\n+        ast.ImportList(\n+            ast.Import(\n+                ast.AttributeList([ast.Attribute(\"Attr1\", 1)]), \"my_import\")),\n+        [\n+            ast.Struct(\n+                'MyStruct', ast.AttributeList(ast.Attribute(\"Attr2\", 2)),\n+                ast.StructBody(\n+                    ast.StructField(\n+                        'a', ast.AttributeList([ast.Attribute(\"Attr3\", 3)]),\n+                        None, 'int32', None))),\n+            ast.Union(\n+                'MyUnion', ast.AttributeList(ast.Attribute(\"Attr4\", 4)),\n+                ast.UnionBody(\n+                    ast.UnionField(\n+                        'a', ast.AttributeList([ast.Attribute(\"Attr5\", 5)]),\n+                        None, 'int32'))),\n+            ast.Enum(\n+                'MyEnum', ast.AttributeList(ast.Attribute(\"Attr6\", 6)),\n+                ast.EnumValueList(\n+                    ast.EnumValue(\n+                        'VALUE', ast.AttributeList([ast.Attribute(\"Attr7\", 7)]),\n+                        None))),\n+            ast.Interface(\n+                'MyInterface', ast.AttributeList(ast.Attribute(\"Attr8\", 8)),\n+                ast.InterfaceBody(\n+                    ast.Method(\n+                        'MyMethod', ast.AttributeList(\n+                            ast.Attribute(\"Attr9\", 9)), None,\n+                        ast.ParameterList(\n+                            ast.Parameter(\n+                                'a',\n+                                ast.AttributeList([ast.Attribute(\"Attr10\", 10)\n+                                                   ]), None, 'int32')),\n+                        ast.ParameterList(\n+                            ast.Parameter(\n+                                'b',\n+                                ast.AttributeList([ast.Attribute(\"Attr11\", 11)\n+                                                   ]), None, 'bool'))))),\n+            ast.Const('kMyConst', ast.AttributeList(\n+                ast.Attribute(\"Attr12\", 12)), 'double', '1.23')\n+        ])\n+    self.assertEquals(parser.Parse(source4, \"my_file.mojom\"), expected4)\n+\n+    # TODO(vtl): Boolean attributes don't work yet. (In fact, we just |eval()|\n+    # literal (non-name) values, which is extremely dubious.)\n+\n+  def testInvalidAttributes(self):\n+    \"\"\"Tests that invalid attributes and attribute lists are correctly\n+    detected.\"\"\"\n+\n+    # Trailing commas not allowed.\n+    source1 = \"[MyAttribute=MyName,] struct MyStruct {};\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:1: Error: Unexpected '\\]':\\n\"\n+        r\"\\[MyAttribute=MyName,\\] struct MyStruct {};$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    # Missing value.\n+    source2 = \"[MyAttribute=] struct MyStruct {};\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:1: Error: Unexpected '\\]':\\n\"\n+        r\"\\[MyAttribute=\\] struct MyStruct {};$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    # Missing key.\n+    source3 = \"[=MyName] struct MyStruct {};\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:1: Error: Unexpected '=':\\n\"\n+        r\"\\[=MyName\\] struct MyStruct {};$\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+  def testValidImports(self):\n+    \"\"\"Tests parsing import statements.\"\"\"\n+\n+    # One import (no module statement).\n+    source1 = \"import \\\"somedir/my.mojom\\\";\"\n+    expected1 = ast.Mojom(None,\n+                          ast.ImportList(ast.Import(None, \"somedir/my.mojom\")),\n+                          [])\n+    self.assertEquals(parser.Parse(source1, \"my_file.mojom\"), expected1)\n+\n+    # Two imports (no module statement).\n+    source2 = \"\"\"\\\n+        import \"somedir/my1.mojom\";\n+        import \"somedir/my2.mojom\";\n+        \"\"\"\n+    expected2 = ast.Mojom(\n+        None,\n+        ast.ImportList([\n+            ast.Import(None, \"somedir/my1.mojom\"),\n+            ast.Import(None, \"somedir/my2.mojom\")\n+        ]), [])\n+    self.assertEquals(parser.Parse(source2, \"my_file.mojom\"), expected2)\n+\n+    # Imports with module statement.\n+    source3 = \"\"\"\\\n+        module my_module;\n+        import \"somedir/my1.mojom\";\n+        import \"somedir/my2.mojom\";\n+        \"\"\"\n+    expected3 = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None),\n+        ast.ImportList([\n+            ast.Import(None, \"somedir/my1.mojom\"),\n+            ast.Import(None, \"somedir/my2.mojom\")\n+        ]), [])\n+    self.assertEquals(parser.Parse(source3, \"my_file.mojom\"), expected3)\n+\n+  def testInvalidImports(self):\n+    \"\"\"Tests that invalid import statements are correctly detected.\"\"\"\n+\n+    source1 = \"\"\"\\\n+        // Make the error occur on line 2.\n+        import invalid\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected 'invalid':\\n\"\n+        r\" *import invalid$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    source2 = \"\"\"\\\n+        import  // Missing string.\n+        struct MyStruct {\n+          int32 a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected 'struct':\\n\"\n+        r\" *struct MyStruct {$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    source3 = \"\"\"\\\n+        import \"foo.mojom\"  // Missing semicolon.\n+        struct MyStruct {\n+          int32 a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected 'struct':\\n\"\n+        r\" *struct MyStruct {$\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+  def testValidNullableTypes(self):\n+    \"\"\"Tests parsing nullable types.\"\"\"\n+\n+    source = \"\"\"\\\n+        struct MyStruct {\n+          int32? a;  // This is actually invalid, but handled at a different\n+                     // level.\n+          string? b;\n+          array<int32> ? c;\n+          array<string ? > ? d;\n+          array<array<int32>?>? e;\n+          array<int32, 1>? f;\n+          array<string?, 1>? g;\n+          some_struct? h;\n+          handle? i;\n+          handle<data_pipe_consumer>? j;\n+          handle<data_pipe_producer>? k;\n+          handle<message_pipe>? l;\n+          handle<shared_buffer>? m;\n+          some_interface&? n;\n+          handle<platform>? o;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody([\n+                ast.StructField('a', None, None, 'int32?', None),\n+                ast.StructField('b', None, None, 'string?', None),\n+                ast.StructField('c', None, None, 'int32[]?', None),\n+                ast.StructField('d', None, None, 'string?[]?', None),\n+                ast.StructField('e', None, None, 'int32[]?[]?', None),\n+                ast.StructField('f', None, None, 'int32[1]?', None),\n+                ast.StructField('g', None, None, 'string?[1]?', None),\n+                ast.StructField('h', None, None, 'some_struct?', None),\n+                ast.StructField('i', None, None, 'handle?', None),\n+                ast.StructField('j', None, None, 'handle<data_pipe_consumer>?',\n+                                None),\n+                ast.StructField('k', None, None, 'handle<data_pipe_producer>?',\n+                                None),\n+                ast.StructField('l', None, None, 'handle<message_pipe>?', None),\n+                ast.StructField('m', None, None, 'handle<shared_buffer>?',\n+                                None),\n+                ast.StructField('n', None, None, 'some_interface&?', None),\n+                ast.StructField('o', None, None, 'handle<platform>?', None)\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source, \"my_file.mojom\"), expected)\n+\n+  def testInvalidNullableTypes(self):\n+    \"\"\"Tests that invalid nullable types are correctly detected.\"\"\"\n+    source1 = \"\"\"\\\n+        struct MyStruct {\n+          string?? a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected '\\?':\\n\"\n+        r\" *string\\?\\? a;$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    source2 = \"\"\"\\\n+        struct MyStruct {\n+          handle?<data_pipe_consumer> a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected '<':\\n\"\n+        r\" *handle\\?<data_pipe_consumer> a;$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    source3 = \"\"\"\\\n+        struct MyStruct {\n+          some_interface?& a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected '&':\\n\"\n+        r\" *some_interface\\?& a;$\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+  def testSimpleUnion(self):\n+    \"\"\"Tests a simple .mojom source that just defines a union.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          int32 a;\n+          double b;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Union(\n+                'MyUnion', None,\n+                ast.UnionBody([\n+                    ast.UnionField('a', None, None, 'int32'),\n+                    ast.UnionField('b', None, None, 'double')\n+                ]))\n+        ])\n+    actual = parser.Parse(source, \"my_file.mojom\")\n+    self.assertEquals(actual, expected)\n+\n+  def testUnionWithOrdinals(self):\n+    \"\"\"Test that ordinals are assigned to fields.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          int32 a @10;\n+          double b @30;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Union(\n+                'MyUnion', None,\n+                ast.UnionBody([\n+                    ast.UnionField('a', None, ast.Ordinal(10), 'int32'),\n+                    ast.UnionField('b', None, ast.Ordinal(30), 'double')\n+                ]))\n+        ])\n+    actual = parser.Parse(source, \"my_file.mojom\")\n+    self.assertEquals(actual, expected)\n+\n+  def testUnionWithStructMembers(self):\n+    \"\"\"Test that struct members are accepted.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          SomeStruct s;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Union(\n+                'MyUnion', None,\n+                ast.UnionBody([ast.UnionField('s', None, None, 'SomeStruct')]))\n+        ])\n+    actual = parser.Parse(source, \"my_file.mojom\")\n+    self.assertEquals(actual, expected)\n+\n+  def testUnionWithArrayMember(self):\n+    \"\"\"Test that array members are accepted.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          array<int32> a;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Union(\n+                'MyUnion', None,\n+                ast.UnionBody([ast.UnionField('a', None, None, 'int32[]')]))\n+        ])\n+    actual = parser.Parse(source, \"my_file.mojom\")\n+    self.assertEquals(actual, expected)\n+\n+  def testUnionWithMapMember(self):\n+    \"\"\"Test that map members are accepted.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          map<int32, string> m;\n+        };\n+        \"\"\"\n+    expected = ast.Mojom(\n+        ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [\n+            ast.Union(\n+                'MyUnion', None,\n+                ast.UnionBody(\n+                    [ast.UnionField('m', None, None, 'string{int32}')]))\n+        ])\n+    actual = parser.Parse(source, \"my_file.mojom\")\n+    self.assertEquals(actual, expected)\n+\n+  def testUnionDisallowNestedStruct(self):\n+    \"\"\"Tests that structs cannot be nested in unions.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          struct MyStruct {\n+            int32 a;\n+          };\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:4: Error: Unexpected 'struct':\\n\"\n+        r\" *struct MyStruct {$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testUnionDisallowNestedInterfaces(self):\n+    \"\"\"Tests that interfaces cannot be nested in unions.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          interface MyInterface {\n+            MyMethod(int32 a);\n+          };\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:4: Error: Unexpected 'interface':\\n\"\n+        r\" *interface MyInterface {$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testUnionDisallowNestedUnion(self):\n+    \"\"\"Tests that unions cannot be nested in unions.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          union MyOtherUnion {\n+            int32 a;\n+          };\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:4: Error: Unexpected 'union':\\n\"\n+        r\" *union MyOtherUnion {$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testUnionDisallowNestedEnum(self):\n+    \"\"\"Tests that enums cannot be nested in unions.\"\"\"\n+    source = \"\"\"\\\n+        module my_module;\n+\n+        union MyUnion {\n+          enum MyEnum {\n+            A,\n+          };\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:4: Error: Unexpected 'enum':\\n\"\n+        r\" *enum MyEnum {$\"):\n+      parser.Parse(source, \"my_file.mojom\")\n+\n+  def testValidAssociatedKinds(self):\n+    \"\"\"Tests parsing associated interfaces and requests.\"\"\"\n+    source1 = \"\"\"\\\n+        struct MyStruct {\n+          associated MyInterface a;\n+          associated MyInterface& b;\n+          associated MyInterface? c;\n+          associated MyInterface&? d;\n+        };\n+        \"\"\"\n+    expected1 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Struct(\n+            'MyStruct', None,\n+            ast.StructBody([\n+                ast.StructField('a', None, None, 'asso<MyInterface>', None),\n+                ast.StructField('b', None, None, 'asso<MyInterface&>', None),\n+                ast.StructField('c', None, None, 'asso<MyInterface>?', None),\n+                ast.StructField('d', None, None, 'asso<MyInterface&>?', None)\n+            ]))\n+    ])\n+    self.assertEquals(parser.Parse(source1, \"my_file.mojom\"), expected1)\n+\n+    source2 = \"\"\"\\\n+        interface MyInterface {\n+          MyMethod(associated A a) =>(associated B& b);\n+        };\"\"\"\n+    expected2 = ast.Mojom(None, ast.ImportList(), [\n+        ast.Interface(\n+            'MyInterface', None,\n+            ast.InterfaceBody(\n+                ast.Method(\n+                    'MyMethod', None, None,\n+                    ast.ParameterList(\n+                        ast.Parameter('a', None, None, 'asso<A>')),\n+                    ast.ParameterList(\n+                        ast.Parameter('b', None, None, 'asso<B&>')))))\n+    ])\n+    self.assertEquals(parser.Parse(source2, \"my_file.mojom\"), expected2)\n+\n+  def testInvalidAssociatedKinds(self):\n+    \"\"\"Tests that invalid associated interfaces and requests are correctly\n+    detected.\"\"\"\n+    source1 = \"\"\"\\\n+        struct MyStruct {\n+          associated associated SomeInterface a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError,\n+        r\"^my_file\\.mojom:2: Error: Unexpected 'associated':\\n\"\n+        r\" *associated associated SomeInterface a;$\"):\n+      parser.Parse(source1, \"my_file.mojom\")\n+\n+    source2 = \"\"\"\\\n+        struct MyStruct {\n+          associated handle a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected 'handle':\\n\"\n+        r\" *associated handle a;$\"):\n+      parser.Parse(source2, \"my_file.mojom\")\n+\n+    source3 = \"\"\"\\\n+        struct MyStruct {\n+          associated? MyInterface& a;\n+        };\n+        \"\"\"\n+    with self.assertRaisesRegexp(\n+        parser.ParseError, r\"^my_file\\.mojom:2: Error: Unexpected '\\?':\\n\"\n+        r\" *associated\\? MyInterface& a;$\"):\n+      parser.Parse(source3, \"my_file.mojom\")\n+\n+\n+if __name__ == \"__main__\":\n+  unittest.main()\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom_parser.py b/utils/ipc/mojo/public/tools/mojom/mojom_parser.py\nnew file mode 100755\nindex 00000000..12adbfb9\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom_parser.py\n@@ -0,0 +1,361 @@\n+#!/usr/bin/env python\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\"\"\"Parses mojom IDL files.\n+\n+This script parses one or more input mojom files and produces corresponding\n+module files fully describing the definitions contained within each mojom. The\n+module data is pickled and can be easily consumed by other tools to, e.g.,\n+generate usable language bindings.\n+\"\"\"\n+\n+import argparse\n+import codecs\n+import errno\n+import json\n+import os\n+import os.path\n+import sys\n+from collections import defaultdict\n+\n+from mojom.generate import module\n+from mojom.generate import translate\n+from mojom.parse import parser\n+from mojom.parse import conditional_features\n+\n+\n+def _ResolveRelativeImportPath(path, roots):\n+  \"\"\"Attempts to resolve a relative import path against a set of possible roots.\n+\n+  Args:\n+    path: The relative import path to resolve.\n+    roots: A list of absolute paths which will be checked in descending length\n+        order for a match against path.\n+\n+  Returns:\n+    A normalized absolute path combining one of the roots with the input path if\n+    and only if such a file exists.\n+\n+  Raises:\n+    ValueError: The path could not be resolved against any of the given roots.\n+  \"\"\"\n+  for root in reversed(sorted(roots, key=len)):\n+    abs_path = os.path.join(root, path)\n+    if os.path.isfile(abs_path):\n+      return os.path.normcase(os.path.normpath(abs_path))\n+\n+  raise ValueError('\"%s\" does not exist in any of %s' % (path, roots))\n+\n+\n+def _RebaseAbsolutePath(path, roots):\n+  \"\"\"Rewrites an absolute file path as relative to an absolute directory path in\n+  roots.\n+\n+  Args:\n+    path: The absolute path of an existing file.\n+    roots: A list of absolute directory paths. The given path argument must fall\n+        within one of these directories.\n+\n+  Returns:\n+    A path equivalent to the input path, but relative to one of the provided\n+    roots. If the input path falls within multiple roots, the longest root is\n+    chosen (and thus the shortest relative path is returned).\n+\n+    Paths returned by this method always use forward slashes as a separator to\n+    mirror mojom import syntax.\n+\n+  Raises:\n+    ValueError if the given path does not fall within any of the listed roots.\n+  \"\"\"\n+  assert os.path.isabs(path)\n+  assert os.path.isfile(path)\n+  assert all(map(os.path.isabs, roots))\n+\n+  sorted_roots = list(reversed(sorted(roots, key=len)))\n+\n+  def try_rebase_path(path, root):\n+    head, rebased_path = os.path.split(path)\n+    while head != root:\n+      head, tail = os.path.split(head)\n+      if not tail:\n+        return None\n+      rebased_path = os.path.join(tail, rebased_path)\n+    return rebased_path\n+\n+  for root in sorted_roots:\n+    relative_path = try_rebase_path(path, root)\n+    if relative_path:\n+      # TODO(crbug.com/953884): Use pathlib for this kind of thing once we're\n+      # fully migrated to Python 3.\n+      return relative_path.replace('\\\\', '/')\n+\n+  raise ValueError('%s does not fall within any of %s' % (path, sorted_roots))\n+\n+\n+def _GetModuleFilename(mojom_filename):\n+  return mojom_filename + '-module'\n+\n+\n+def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\n+                       dependencies, loaded_modules):\n+  \"\"\"Recursively ensures that a module and its dependencies are loaded.\n+\n+  Args:\n+    mojom_abspath: An absolute file path pointing to a mojom file to load.\n+    module_path: The relative path used to identify mojom_abspath.\n+    abs_paths: A mapping from module paths to absolute file paths for all\n+        inputs given to this execution of the script.\n+    asts: A map from each input mojom's absolute path to its parsed AST.\n+    dependencies: A mapping of which input mojoms depend on each other, indexed\n+        by absolute file path.\n+    loaded_modules: A mapping of all modules loaded so far, including non-input\n+        modules that were pulled in as transitive dependencies of the inputs.\n+    import_set: The working set of mojom imports processed so far in this\n+        call stack. Used to detect circular dependencies.\n+    import_stack: An ordered list of imports processed so far in this call\n+        stack. Used to report circular dependencies.\n+\n+  Returns:\n+    None\n+\n+    On return, loaded_modules will be populated with the loaded input mojom's\n+    Module as well as the Modules of all of its transitive dependencies.\"\"\"\n+\n+  if mojom_abspath in loaded_modules:\n+    # Already done.\n+    return\n+\n+  for dep_abspath, dep_path in dependencies[mojom_abspath]:\n+    if dep_abspath not in loaded_modules:\n+      _EnsureInputLoaded(dep_abspath, dep_path, abs_paths, asts, dependencies,\n+                         loaded_modules)\n+\n+  imports = {}\n+  for imp in asts[mojom_abspath].import_list:\n+    path = imp.import_filename\n+    imports[path] = loaded_modules[abs_paths[path]]\n+  loaded_modules[mojom_abspath] = translate.OrderedModule(\n+      asts[mojom_abspath], module_path, imports)\n+\n+\n+def _CollectAllowedImportsFromBuildMetadata(build_metadata_filename):\n+  allowed_imports = set()\n+  processed_deps = set()\n+\n+  def collect(metadata_filename):\n+    processed_deps.add(metadata_filename)\n+    with open(metadata_filename) as f:\n+      metadata = json.load(f)\n+      allowed_imports.update(\n+          map(os.path.normcase, map(os.path.normpath, metadata['sources'])))\n+      for dep_metadata in metadata['deps']:\n+        if dep_metadata not in processed_deps:\n+          collect(dep_metadata)\n+\n+  collect(build_metadata_filename)\n+  return allowed_imports\n+\n+\n+def _ParseMojoms(mojom_files,\n+                 input_root_paths,\n+                 output_root_path,\n+                 enabled_features,\n+                 allowed_imports=None):\n+  \"\"\"Parses a set of mojom files and produces serialized module outputs.\n+\n+  Args:\n+    mojom_files: A list of mojom files to process. Paths must be absolute paths\n+        which fall within one of the input or output root paths.\n+    input_root_paths: A list of absolute filesystem paths which may be used to\n+        resolve relative mojom file paths.\n+    output_root_path: An absolute filesystem path which will service as the root\n+        for all emitted artifacts. Artifacts produced from a given mojom file\n+        are based on the mojom's relative path, rebased onto this path.\n+        Additionally, the script expects this root to contain already-generated\n+        modules for any transitive dependencies not listed in mojom_files.\n+    enabled_features: A list of enabled feature names, controlling which AST\n+        nodes are filtered by [EnableIf] attributes.\n+\n+  Returns:\n+    None.\n+\n+    Upon completion, a mojom-module file will be saved for each input mojom.\n+  \"\"\"\n+  assert input_root_paths\n+  assert output_root_path\n+\n+  loaded_mojom_asts = {}\n+  loaded_modules = {}\n+  input_dependencies = defaultdict(set)\n+  mojom_files_to_parse = dict((os.path.normcase(abs_path),\n+                               _RebaseAbsolutePath(abs_path, input_root_paths))\n+                              for abs_path in mojom_files)\n+  abs_paths = dict(\n+      (path, abs_path) for abs_path, path in mojom_files_to_parse.items())\n+  for mojom_abspath, _ in mojom_files_to_parse.items():\n+    with codecs.open(mojom_abspath, encoding='utf-8') as f:\n+      ast = parser.Parse(''.join(f.readlines()), mojom_abspath)\n+      conditional_features.RemoveDisabledDefinitions(ast, enabled_features)\n+      loaded_mojom_asts[mojom_abspath] = ast\n+      invalid_imports = []\n+      for imp in ast.import_list:\n+        import_abspath = _ResolveRelativeImportPath(imp.import_filename,\n+                                                    input_root_paths)\n+        if allowed_imports and import_abspath not in allowed_imports:\n+          invalid_imports.append(imp.import_filename)\n+\n+        abs_paths[imp.import_filename] = import_abspath\n+        if import_abspath in mojom_files_to_parse:\n+          # This import is in the input list, so we're going to translate it\n+          # into a module below; however it's also a dependency of another input\n+          # module. We retain record of dependencies to help with input\n+          # processing later.\n+          input_dependencies[mojom_abspath].add((import_abspath,\n+                                                 imp.import_filename))\n+        else:\n+          # We have an import that isn't being parsed right now. It must already\n+          # be parsed and have a module file sitting in a corresponding output\n+          # location.\n+          module_path = _GetModuleFilename(imp.import_filename)\n+          module_abspath = _ResolveRelativeImportPath(module_path,\n+                                                      [output_root_path])\n+          with open(module_abspath, 'rb') as module_file:\n+            loaded_modules[import_abspath] = module.Module.Load(module_file)\n+\n+      if invalid_imports:\n+        raise ValueError(\n+            '\\nThe file %s imports the following files not allowed by build '\n+            'dependencies:\\n\\n%s\\n' % (mojom_abspath,\n+                                       '\\n'.join(invalid_imports)))\n+\n+\n+  # At this point all transitive imports not listed as inputs have been loaded\n+  # and we have a complete dependency tree of the unprocessed inputs. Now we can\n+  # load all the inputs, resolving dependencies among them recursively as we go.\n+  num_existing_modules_loaded = len(loaded_modules)\n+  for mojom_abspath, mojom_path in mojom_files_to_parse.items():\n+    _EnsureInputLoaded(mojom_abspath, mojom_path, abs_paths, loaded_mojom_asts,\n+                       input_dependencies, loaded_modules)\n+  assert (num_existing_modules_loaded +\n+          len(mojom_files_to_parse) == len(loaded_modules))\n+\n+  # Now we have fully translated modules for every input and every transitive\n+  # dependency. We can dump the modules to disk for other tools to use.\n+  for mojom_abspath, mojom_path in mojom_files_to_parse.items():\n+    module_path = os.path.join(output_root_path, _GetModuleFilename(mojom_path))\n+    module_dir = os.path.dirname(module_path)\n+    if not os.path.exists(module_dir):\n+      try:\n+        # Python 2 doesn't support exist_ok on makedirs(), so we just ignore\n+        # that failure if it happens. It's possible during build due to races\n+        # among build steps with module outputs in the same directory.\n+        os.makedirs(module_dir)\n+      except OSError as e:\n+        if e.errno != errno.EEXIST:\n+          raise\n+    with open(module_path, 'wb') as f:\n+      loaded_modules[mojom_abspath].Dump(f)\n+\n+\n+def Run(command_line):\n+  arg_parser = argparse.ArgumentParser(\n+      description=\"\"\"\n+Parses one or more mojom files and produces corresponding module outputs fully\n+describing the definitions therein. The output is exhaustive, stable, and\n+sufficient for another tool to consume and emit e.g. usable language\n+bindings based on the original mojoms.\"\"\",\n+      epilog=\"\"\"\n+Note that each transitive import dependency reachable from the input mojoms must\n+either also be listed as an input or must have its corresponding compiled module\n+already present in the provided output root.\"\"\")\n+\n+  arg_parser.add_argument(\n+      '--input-root',\n+      default=[],\n+      action='append',\n+      metavar='ROOT',\n+      dest='input_root_paths',\n+      help='Adds ROOT to the set of root paths against which relative input '\n+      'paths should be resolved. Provided root paths are always searched '\n+      'in order from longest absolute path to shortest.')\n+  arg_parser.add_argument(\n+      '--output-root',\n+      action='store',\n+      required=True,\n+      dest='output_root_path',\n+      metavar='ROOT',\n+      help='Use ROOT as the root path in which the parser should emit compiled '\n+      'modules for each processed input mojom. The path of emitted module is '\n+      'based on the relative input path, rebased onto this root. Note that '\n+      'ROOT is also searched for existing modules of any transitive imports '\n+      'which were not included in the set of inputs.')\n+  arg_parser.add_argument(\n+      '--mojoms',\n+      nargs='+',\n+      dest='mojom_files',\n+      default=[],\n+      metavar='MOJOM_FILE',\n+      help='Input mojom filename(s). Each filename must be either an absolute '\n+      'path which falls within one of the given input or output roots, or a '\n+      'relative path the parser will attempt to resolve using each of those '\n+      'roots in unspecified order.')\n+  arg_parser.add_argument(\n+      '--mojom-file-list',\n+      action='store',\n+      metavar='LIST_FILENAME',\n+      help='Input file whose contents are a list of mojoms to process. This '\n+      'may be provided in lieu of --mojoms to avoid hitting command line '\n+      'length limtations')\n+  arg_parser.add_argument(\n+      '--enable-feature',\n+      dest='enabled_features',\n+      default=[],\n+      action='append',\n+      metavar='FEATURE',\n+      help='Enables a named feature when parsing the given mojoms. Features '\n+      'are identified by arbitrary string values. Specifying this flag with a '\n+      'given FEATURE name will cause the parser to process any syntax elements '\n+      'tagged with an [EnableIf=FEATURE] attribute. If this flag is not '\n+      'provided for a given FEATURE, such tagged elements are discarded by the '\n+      'parser and will not be present in the compiled output.')\n+  arg_parser.add_argument(\n+      '--check-imports',\n+      dest='build_metadata_filename',\n+      action='store',\n+      metavar='METADATA_FILENAME',\n+      help='Instructs the parser to check imports against a set of allowed '\n+      'imports. Allowed imports are based on build metadata within '\n+      'METADATA_FILENAME. This is a JSON file with a `sources` key listing '\n+      'paths to the set of input mojom files being processed by this parser '\n+      'run, and a `deps` key listing paths to metadata files for any '\n+      'dependencies of these inputs. This feature can be used to implement '\n+      'build-time dependency checking for mojom imports, where each build '\n+      'metadata file corresponds to a build target in the dependency graph of '\n+      'a typical build system.')\n+\n+  args, _ = arg_parser.parse_known_args(command_line)\n+  if args.mojom_file_list:\n+    with open(args.mojom_file_list) as f:\n+      args.mojom_files.extend(f.read().split())\n+\n+  if not args.mojom_files:\n+    raise ValueError(\n+        'Must list at least one mojom file via --mojoms or --mojom-file-list')\n+\n+  mojom_files = list(map(os.path.abspath, args.mojom_files))\n+  input_roots = list(map(os.path.abspath, args.input_root_paths))\n+  output_root = os.path.abspath(args.output_root_path)\n+\n+  if args.build_metadata_filename:\n+    allowed_imports = _CollectAllowedImportsFromBuildMetadata(\n+        args.build_metadata_filename)\n+  else:\n+    allowed_imports = None\n+\n+  _ParseMojoms(mojom_files, input_roots, output_root, args.enabled_features,\n+               allowed_imports)\n+\n+\n+if __name__ == '__main__':\n+  Run(sys.argv[1:])\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom_parser_test_case.py b/utils/ipc/mojo/public/tools/mojom/mojom_parser_test_case.py\nnew file mode 100644\nindex 00000000..e213fbfa\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom_parser_test_case.py\n@@ -0,0 +1,73 @@\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import json\n+import os\n+import os.path\n+import shutil\n+import tempfile\n+import unittest\n+\n+import mojom_parser\n+\n+from mojom.generate import module\n+\n+\n+class MojomParserTestCase(unittest.TestCase):\n+  \"\"\"Tests covering the behavior defined by the main mojom_parser.py script.\n+  This includes behavior around input and output path manipulation, dependency\n+  resolution, and module serialization and deserialization.\"\"\"\n+\n+  def __init__(self, method_name):\n+    super(MojomParserTestCase, self).__init__(method_name)\n+    self._temp_dir = None\n+\n+  def setUp(self):\n+    self._temp_dir = tempfile.mkdtemp()\n+\n+  def tearDown(self):\n+    shutil.rmtree(self._temp_dir)\n+    self._temp_dir = None\n+\n+  def GetPath(self, path):\n+    assert not os.path.isabs(path)\n+    return os.path.join(self._temp_dir, path)\n+\n+  def GetModulePath(self, path):\n+    assert not os.path.isabs(path)\n+    return os.path.join(self.GetPath('out'), path) + '-module'\n+\n+  def WriteFile(self, path, contents):\n+    full_path = self.GetPath(path)\n+    dirname = os.path.dirname(full_path)\n+    if not os.path.exists(dirname):\n+      os.makedirs(dirname)\n+    with open(full_path, 'w') as f:\n+      f.write(contents)\n+\n+  def LoadModule(self, mojom_path):\n+    with open(self.GetModulePath(mojom_path), 'rb') as f:\n+      return module.Module.Load(f)\n+\n+  def ParseMojoms(self, mojoms, metadata=None):\n+    \"\"\"Parse all input mojoms relative the temp dir.\"\"\"\n+    out_dir = self.GetPath('out')\n+    args = [\n+        '--input-root', self._temp_dir, '--input-root', out_dir,\n+        '--output-root', out_dir, '--mojoms'\n+    ] + list(map(lambda mojom: os.path.join(self._temp_dir, mojom), mojoms))\n+    if metadata:\n+      args.extend(['--check-imports', self.GetPath(metadata)])\n+    mojom_parser.Run(args)\n+\n+  def ExtractTypes(self, mojom):\n+    filename = 'test.mojom'\n+    self.WriteFile(filename, mojom)\n+    self.ParseMojoms([filename])\n+    m = self.LoadModule(filename)\n+    definitions = {}\n+    for kinds in (m.enums, m.structs, m.unions, m.interfaces):\n+      for kind in kinds:\n+        definitions[kind.mojom_name] = kind\n+    return definitions\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom_parser_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom_parser_unittest.py\nnew file mode 100644\nindex 00000000..a93f34ba\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom_parser_unittest.py\n@@ -0,0 +1,171 @@\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+from mojom_parser_test_case import MojomParserTestCase\n+\n+\n+class MojomParserTest(MojomParserTestCase):\n+  \"\"\"Tests covering the behavior defined by the main mojom_parser.py script.\n+  This includes behavior around input and output path manipulation, dependency\n+  resolution, and module serialization and deserialization.\"\"\"\n+\n+  def testBasicParse(self):\n+    \"\"\"Basic test to verify that we can parse a mojom file and get a module.\"\"\"\n+    mojom = 'foo/bar.mojom'\n+    self.WriteFile(\n+        mojom, \"\"\"\\\n+        module test;\n+        enum TestEnum { kFoo };\n+        \"\"\")\n+    self.ParseMojoms([mojom])\n+\n+    m = self.LoadModule(mojom)\n+    self.assertEqual('foo/bar.mojom', m.path)\n+    self.assertEqual('test', m.mojom_namespace)\n+    self.assertEqual(1, len(m.enums))\n+\n+  def testBasicParseWithAbsolutePaths(self):\n+    \"\"\"Verifies that we can parse a mojom file given an absolute path input.\"\"\"\n+    mojom = 'foo/bar.mojom'\n+    self.WriteFile(\n+        mojom, \"\"\"\\\n+        module test;\n+        enum TestEnum { kFoo };\n+        \"\"\")\n+    self.ParseMojoms([self.GetPath(mojom)])\n+\n+    m = self.LoadModule(mojom)\n+    self.assertEqual('foo/bar.mojom', m.path)\n+    self.assertEqual('test', m.mojom_namespace)\n+    self.assertEqual(1, len(m.enums))\n+\n+  def testImport(self):\n+    \"\"\"Verify imports within the same set of mojom inputs.\"\"\"\n+    a = 'a.mojom'\n+    b = 'b.mojom'\n+    self.WriteFile(\n+        a, \"\"\"\\\n+        module a;\n+        import \"b.mojom\";\n+        struct Foo { b.Bar bar; };\"\"\")\n+    self.WriteFile(b, \"\"\"\\\n+        module b;\n+        struct Bar {};\"\"\")\n+    self.ParseMojoms([a, b])\n+\n+    ma = self.LoadModule(a)\n+    mb = self.LoadModule(b)\n+    self.assertEqual('a.mojom', ma.path)\n+    self.assertEqual('b.mojom', mb.path)\n+    self.assertEqual(1, len(ma.imports))\n+    self.assertEqual(mb, ma.imports[0])\n+\n+  def testPreProcessedImport(self):\n+    \"\"\"Verify imports processed by a previous parser execution can be loaded\n+    properly when parsing a dependent mojom.\"\"\"\n+    a = 'a.mojom'\n+    self.WriteFile(a, \"\"\"\\\n+        module a;\n+        struct Bar {};\"\"\")\n+    self.ParseMojoms([a])\n+\n+    b = 'b.mojom'\n+    self.WriteFile(\n+        b, \"\"\"\\\n+        module b;\n+        import \"a.mojom\";\n+        struct Foo { a.Bar bar; };\"\"\")\n+    self.ParseMojoms([b])\n+\n+  def testMissingImport(self):\n+    \"\"\"Verify that an import fails if the imported mojom does not exist.\"\"\"\n+    a = 'a.mojom'\n+    self.WriteFile(\n+        a, \"\"\"\\\n+        module a;\n+        import \"non-existent.mojom\";\n+        struct Bar {};\"\"\")\n+    with self.assertRaisesRegexp(ValueError, \"does not exist\"):\n+      self.ParseMojoms([a])\n+\n+  def testUnparsedImport(self):\n+    \"\"\"Verify that an import fails if the imported mojom is not in the set of\n+    mojoms provided to the parser on this execution AND there is no pre-existing\n+    parsed output module already on disk for it.\"\"\"\n+    a = 'a.mojom'\n+    b = 'b.mojom'\n+    self.WriteFile(a, \"\"\"\\\n+        module a;\n+        struct Bar {};\"\"\")\n+    self.WriteFile(\n+        b, \"\"\"\\\n+        module b;\n+        import \"a.mojom\";\n+        struct Foo { a.Bar bar; };\"\"\")\n+\n+    # a.mojom has not been parsed yet, so its import will fail when processing\n+    # b.mojom here.\n+    with self.assertRaisesRegexp(ValueError, \"does not exist\"):\n+      self.ParseMojoms([b])\n+\n+  def testCheckImportsBasic(self):\n+    \"\"\"Verify that the parser can handle --check-imports with a valid set of\n+    inputs, including support for transitive dependency resolution.\"\"\"\n+    a = 'a.mojom'\n+    a_metadata = 'out/a.build_metadata'\n+    b = 'b.mojom'\n+    b_metadata = 'out/b.build_metadata'\n+    c = 'c.mojom'\n+    c_metadata = 'out/c.build_metadata'\n+    self.WriteFile(a_metadata,\n+                   '{\"sources\": [\"%s\"], \"deps\": []}\\n' % self.GetPath(a))\n+    self.WriteFile(\n+        b_metadata,\n+        '{\"sources\": [\"%s\"], \"deps\": [\"%s\"]}\\n' % (self.GetPath(b),\n+                                                   self.GetPath(a_metadata)))\n+    self.WriteFile(\n+        c_metadata,\n+        '{\"sources\": [\"%s\"], \"deps\": [\"%s\"]}\\n' % (self.GetPath(c),\n+                                                   self.GetPath(b_metadata)))\n+    self.WriteFile(a, \"\"\"\\\n+        module a;\n+        struct Bar {};\"\"\")\n+    self.WriteFile(\n+        b, \"\"\"\\\n+        module b;\n+        import \"a.mojom\";\n+        struct Foo { a.Bar bar; };\"\"\")\n+    self.WriteFile(\n+        c, \"\"\"\\\n+        module c;\n+        import \"a.mojom\";\n+        import \"b.mojom\";\n+        struct Baz { b.Foo foo; };\"\"\")\n+    self.ParseMojoms([a], metadata=a_metadata)\n+    self.ParseMojoms([b], metadata=b_metadata)\n+    self.ParseMojoms([c], metadata=c_metadata)\n+\n+  def testCheckImportsMissing(self):\n+    \"\"\"Verify that the parser rejects valid input mojoms when imports don't\n+    agree with build metadata given via --check-imports.\"\"\"\n+    a = 'a.mojom'\n+    a_metadata = 'out/a.build_metadata'\n+    b = 'b.mojom'\n+    b_metadata = 'out/b.build_metadata'\n+    self.WriteFile(a_metadata,\n+                   '{\"sources\": [\"%s\"], \"deps\": []}\\n' % self.GetPath(a))\n+    self.WriteFile(b_metadata,\n+                   '{\"sources\": [\"%s\"], \"deps\": []}\\n' % self.GetPath(b))\n+    self.WriteFile(a, \"\"\"\\\n+        module a;\n+        struct Bar {};\"\"\")\n+    self.WriteFile(\n+        b, \"\"\"\\\n+        module b;\n+        import \"a.mojom\";\n+        struct Foo { a.Bar bar; };\"\"\")\n+\n+    self.ParseMojoms([a], metadata=a_metadata)\n+    with self.assertRaisesRegexp(ValueError, \"not allowed by build\"):\n+      self.ParseMojoms([b], metadata=b_metadata)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/stable_attribute_unittest.py b/utils/ipc/mojo/public/tools/mojom/stable_attribute_unittest.py\nnew file mode 100644\nindex 00000000..d45ec586\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/stable_attribute_unittest.py\n@@ -0,0 +1,127 @@\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+from mojom_parser_test_case import MojomParserTestCase\n+\n+from mojom.generate import module\n+\n+\n+class StableAttributeTest(MojomParserTestCase):\n+  \"\"\"Tests covering usage of the [Stable] attribute.\"\"\"\n+\n+  def testStableAttributeTagging(self):\n+    \"\"\"Verify that we recognize the [Stable] attribute on relevant definitions\n+    and the resulting parser outputs are tagged accordingly.\"\"\"\n+    mojom = 'test.mojom'\n+    self.WriteFile(\n+        mojom, \"\"\"\\\n+        [Stable] enum TestEnum { kFoo };\n+        enum UnstableEnum { kBar };\n+        [Stable] struct TestStruct { TestEnum a; };\n+        struct UnstableStruct { UnstableEnum a; };\n+        [Stable] union TestUnion { TestEnum a; TestStruct b; };\n+        union UnstableUnion { UnstableEnum a; UnstableStruct b; };\n+        [Stable] interface TestInterface { Foo@0(TestUnion x) => (); };\n+        interface UnstableInterface { Foo(UnstableUnion x) => (); };\n+        \"\"\")\n+    self.ParseMojoms([mojom])\n+\n+    m = self.LoadModule(mojom)\n+    self.assertEqual(2, len(m.enums))\n+    self.assertTrue(m.enums[0].stable)\n+    self.assertFalse(m.enums[1].stable)\n+    self.assertEqual(2, len(m.structs))\n+    self.assertTrue(m.structs[0].stable)\n+    self.assertFalse(m.structs[1].stable)\n+    self.assertEqual(2, len(m.unions))\n+    self.assertTrue(m.unions[0].stable)\n+    self.assertFalse(m.unions[1].stable)\n+    self.assertEqual(2, len(m.interfaces))\n+    self.assertTrue(m.interfaces[0].stable)\n+    self.assertFalse(m.interfaces[1].stable)\n+\n+  def testStableStruct(self):\n+    \"\"\"A [Stable] struct is valid if all its fields are also stable.\"\"\"\n+    self.ExtractTypes('[Stable] struct S {};')\n+    self.ExtractTypes('[Stable] struct S { int32 x; bool b; };')\n+    self.ExtractTypes('[Stable] enum E { A }; [Stable] struct S { E e; };')\n+    self.ExtractTypes('[Stable] struct S {}; [Stable] struct T { S s; };')\n+    self.ExtractTypes(\n+        '[Stable] struct S {}; [Stable] struct T { array<S> ss; };')\n+    self.ExtractTypes(\n+        '[Stable] interface F {}; [Stable] struct T { pending_remote<F> f; };')\n+\n+    with self.assertRaisesRegexp(Exception, 'because it depends on E'):\n+      self.ExtractTypes('enum E { A }; [Stable] struct S { E e; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on X'):\n+      self.ExtractTypes('struct X {}; [Stable] struct S { X x; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on T'):\n+      self.ExtractTypes('struct T {}; [Stable] struct S { array<T> xs; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on T'):\n+      self.ExtractTypes('struct T {}; [Stable] struct S { map<int32, T> xs; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on T'):\n+      self.ExtractTypes('struct T {}; [Stable] struct S { map<T, int32> xs; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on F'):\n+      self.ExtractTypes(\n+          'interface F {}; [Stable] struct S { pending_remote<F> f; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on F'):\n+      self.ExtractTypes(\n+          'interface F {}; [Stable] struct S { pending_receiver<F> f; };')\n+\n+  def testStableUnion(self):\n+    \"\"\"A [Stable] union is valid if all its fields' types are also stable.\"\"\"\n+    self.ExtractTypes('[Stable] union U {};')\n+    self.ExtractTypes('[Stable] union U { int32 x; bool b; };')\n+    self.ExtractTypes('[Stable] enum E { A }; [Stable] union U { E e; };')\n+    self.ExtractTypes('[Stable] struct S {}; [Stable] union U { S s; };')\n+    self.ExtractTypes(\n+        '[Stable] struct S {}; [Stable] union U { array<S> ss; };')\n+    self.ExtractTypes(\n+        '[Stable] interface F {}; [Stable] union U { pending_remote<F> f; };')\n+\n+    with self.assertRaisesRegexp(Exception, 'because it depends on E'):\n+      self.ExtractTypes('enum E { A }; [Stable] union U { E e; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on X'):\n+      self.ExtractTypes('struct X {}; [Stable] union U { X x; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on T'):\n+      self.ExtractTypes('struct T {}; [Stable] union U { array<T> xs; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on T'):\n+      self.ExtractTypes('struct T {}; [Stable] union U { map<int32, T> xs; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on T'):\n+      self.ExtractTypes('struct T {}; [Stable] union U { map<T, int32> xs; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on F'):\n+      self.ExtractTypes(\n+          'interface F {}; [Stable] union U { pending_remote<F> f; };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on F'):\n+      self.ExtractTypes(\n+          'interface F {}; [Stable] union U { pending_receiver<F> f; };')\n+\n+  def testStableInterface(self):\n+    \"\"\"A [Stable] interface is valid if all its methods' parameter types are\n+    stable, including response parameters where applicable.\"\"\"\n+    self.ExtractTypes('[Stable] interface F {};')\n+    self.ExtractTypes('[Stable] interface F { A@0(int32 x); };')\n+    self.ExtractTypes('[Stable] interface F { A@0(int32 x) => (bool b); };')\n+    self.ExtractTypes(\"\"\"\\\n+        [Stable] enum E { A, B, C };\n+        [Stable] struct S {};\n+        [Stable] interface F { A@0(E e, S s) => (bool b, array<S> s); };\n+        \"\"\")\n+\n+    with self.assertRaisesRegexp(Exception, 'because it depends on E'):\n+      self.ExtractTypes(\n+          'enum E { A, B, C }; [Stable] interface F { A@0(E e); };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on E'):\n+      self.ExtractTypes(\n+          'enum E { A, B, C }; [Stable] interface F { A@0(int32 x) => (E e); };'\n+      )\n+    with self.assertRaisesRegexp(Exception, 'because it depends on S'):\n+      self.ExtractTypes(\n+          'struct S {}; [Stable] interface F { A@0(int32 x) => (S s); };')\n+    with self.assertRaisesRegexp(Exception, 'because it depends on S'):\n+      self.ExtractTypes(\n+          'struct S {}; [Stable] interface F { A@0(S s) => (bool b); };')\n+\n+    with self.assertRaisesRegexp(Exception, 'explicit method ordinals'):\n+      self.ExtractTypes('[Stable] interface F { A() => (); };')\ndiff --git a/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py b/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\nnew file mode 100644\nindex 00000000..a0ee150e\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\n@@ -0,0 +1,397 @@\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+from mojom_parser_test_case import MojomParserTestCase\n+\n+\n+class VersionCompatibilityTest(MojomParserTestCase):\n+  \"\"\"Tests covering compatibility between two versions of the same mojom type\n+  definition. This coverage ensures that we can reliably detect unsafe changes\n+  to definitions that are expected to tolerate version skew in production\n+  environments.\"\"\"\n+\n+  def _GetTypeCompatibilityMap(self, old_mojom, new_mojom):\n+    \"\"\"Helper to support the implementation of assertBackwardCompatible and\n+    assertNotBackwardCompatible.\"\"\"\n+\n+    old = self.ExtractTypes(old_mojom)\n+    new = self.ExtractTypes(new_mojom)\n+    self.assertEqual(set(old.keys()), set(new.keys()),\n+                     'Old and new test mojoms should use the same type names.')\n+\n+    compatibility_map = {}\n+    for name in old.keys():\n+      compatibility_map[name] = new[name].IsBackwardCompatible(old[name])\n+    return compatibility_map\n+\n+  def assertBackwardCompatible(self, old_mojom, new_mojom):\n+    compatibility_map = self._GetTypeCompatibilityMap(old_mojom, new_mojom)\n+    for name, compatible in compatibility_map.items():\n+      if not compatible:\n+        raise AssertionError(\n+            'Given the old mojom:\\n\\n    %s\\n\\nand the new mojom:\\n\\n    %s\\n\\n'\n+            'The new definition of %s should pass a backward-compatibiity '\n+            'check, but it does not.' % (old_mojom, new_mojom, name))\n+\n+  def assertNotBackwardCompatible(self, old_mojom, new_mojom):\n+    compatibility_map = self._GetTypeCompatibilityMap(old_mojom, new_mojom)\n+    if all(compatibility_map.values()):\n+      raise AssertionError(\n+          'Given the old mojom:\\n\\n    %s\\n\\nand the new mojom:\\n\\n    %s\\n\\n'\n+          'The new mojom should fail a backward-compatibility check, but it '\n+          'does not.' % (old_mojom, new_mojom))\n+\n+  def testNewNonExtensibleEnumValue(self):\n+    \"\"\"Adding a value to a non-extensible enum breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('enum E { kFoo, kBar };',\n+                                     'enum E { kFoo, kBar, kBaz };')\n+\n+  def testNewNonExtensibleEnumValueWithMinVersion(self):\n+    \"\"\"Adding a value to a non-extensible enum breaks backward-compatibility,\n+    even with a new [MinVersion] specified for the value.\"\"\"\n+    self.assertNotBackwardCompatible(\n+        'enum E { kFoo, kBar };', 'enum E { kFoo, kBar, [MinVersion=1] kBaz };')\n+\n+  def testNewValueInExistingVersion(self):\n+    \"\"\"Adding a value to an existing version is not allowed, even if the old\n+    enum was marked [Extensible]. Note that it is irrelevant whether or not the\n+    new enum is marked [Extensible].\"\"\"\n+    self.assertNotBackwardCompatible('[Extensible] enum E { kFoo, kBar };',\n+                                     'enum E { kFoo, kBar, kBaz };')\n+    self.assertNotBackwardCompatible(\n+        '[Extensible] enum E { kFoo, kBar };',\n+        '[Extensible] enum E { kFoo, kBar, kBaz };')\n+    self.assertNotBackwardCompatible(\n+        '[Extensible] enum E { kFoo, [MinVersion=1] kBar };',\n+        'enum E { kFoo, [MinVersion=1] kBar, [MinVersion=1] kBaz };')\n+\n+  def testEnumValueRemoval(self):\n+    \"\"\"Removal of an enum value is never valid even for [Extensible] enums.\"\"\"\n+    self.assertNotBackwardCompatible('enum E { kFoo, kBar };',\n+                                     'enum E { kFoo };')\n+    self.assertNotBackwardCompatible('[Extensible] enum E { kFoo, kBar };',\n+                                     '[Extensible] enum E { kFoo };')\n+    self.assertNotBackwardCompatible(\n+        '[Extensible] enum E { kA, [MinVersion=1] kB };',\n+        '[Extensible] enum E { kA, };')\n+    self.assertNotBackwardCompatible(\n+        '[Extensible] enum E { kA, [MinVersion=1] kB, [MinVersion=1] kZ };',\n+        '[Extensible] enum E { kA, [MinVersion=1] kB };')\n+\n+  def testNewExtensibleEnumValueWithMinVersion(self):\n+    \"\"\"Adding a new and properly [MinVersion]'d value to an [Extensible] enum\n+    is a backward-compatible change. Note that it is irrelevant whether or not\n+    the new enum is marked [Extensible].\"\"\"\n+    self.assertBackwardCompatible('[Extensible] enum E { kA, kB };',\n+                                  'enum E { kA, kB, [MinVersion=1] kC };')\n+    self.assertBackwardCompatible(\n+        '[Extensible] enum E { kA, kB };',\n+        '[Extensible] enum E { kA, kB, [MinVersion=1] kC };')\n+    self.assertBackwardCompatible(\n+        '[Extensible] enum E { kA, [MinVersion=1] kB };',\n+        '[Extensible] enum E { kA, [MinVersion=1] kB, [MinVersion=2] kC };')\n+\n+  def testRenameEnumValue(self):\n+    \"\"\"Renaming an enum value does not affect backward-compatibility. Only\n+    numeric value is relevant.\"\"\"\n+    self.assertBackwardCompatible('enum E { kA, kB };', 'enum E { kX, kY };')\n+\n+  def testAddEnumValueAlias(self):\n+    \"\"\"Adding new enum fields does not affect backward-compatibility if it does\n+    not introduce any new numeric values.\"\"\"\n+    self.assertBackwardCompatible(\n+        'enum E { kA, kB };', 'enum E { kA, kB, kC = kA, kD = 1, kE = kD };')\n+\n+  def testEnumIdentity(self):\n+    \"\"\"An unchanged enum is obviously backward-compatible.\"\"\"\n+    self.assertBackwardCompatible('enum E { kA, kB, kC };',\n+                                  'enum E { kA, kB, kC };')\n+\n+  def testNewStructFieldUnversioned(self):\n+    \"\"\"Adding a new field to a struct without a new (i.e. higher than any\n+    existing version) [MinVersion] tag breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('struct S { string a; };',\n+                                     'struct S { string a; string b; };')\n+\n+  def testStructFieldRemoval(self):\n+    \"\"\"Removing a field from a struct breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('struct S { string a; string b; };',\n+                                     'struct S { string a; };')\n+\n+  def testStructFieldTypeChange(self):\n+    \"\"\"Changing the type of an existing field always breaks\n+    backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('struct S { string a; };',\n+                                     'struct S { array<int32> a; };')\n+\n+  def testStructFieldBecomingOptional(self):\n+    \"\"\"Changing a field from non-optional to optional breaks\n+    backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('struct S { string a; };',\n+                                     'struct S { string? a; };')\n+\n+  def testStructFieldBecomingNonOptional(self):\n+    \"\"\"Changing a field from optional to non-optional breaks\n+    backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('struct S { string? a; };',\n+                                     'struct S { string a; };')\n+\n+  def testStructFieldOrderChange(self):\n+    \"\"\"Changing the order of fields breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('struct S { string a; bool b; };',\n+                                     'struct S { bool b; string a; };')\n+    self.assertNotBackwardCompatible('struct S { string a@0; bool b@1; };',\n+                                     'struct S { string a@1; bool b@0; };')\n+\n+  def testStructFieldMinVersionChange(self):\n+    \"\"\"Changing the MinVersion of a field breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible(\n+        'struct S { string a; [MinVersion=1] string? b; };',\n+        'struct S { string a; [MinVersion=2] string? b; };')\n+\n+  def testStructFieldTypeChange(self):\n+    \"\"\"If a struct field's own type definition changes, the containing struct\n+    is backward-compatible if and only if the field type's change is\n+    backward-compatible.\"\"\"\n+    self.assertBackwardCompatible(\n+        'struct S {}; struct T { S s; };',\n+        'struct S { [MinVersion=1] int32 x; }; struct T { S s; };')\n+    self.assertBackwardCompatible(\n+        '[Extensible] enum E { kA }; struct S { E e; };',\n+        '[Extensible] enum E { kA, [MinVersion=1] kB }; struct S { E e; };')\n+    self.assertNotBackwardCompatible(\n+        'struct S {}; struct T { S s; };',\n+        'struct S { int32 x; }; struct T { S s; };')\n+    self.assertNotBackwardCompatible(\n+        '[Extensible] enum E { kA }; struct S { E e; };',\n+        '[Extensible] enum E { kA, kB }; struct S { E e; };')\n+\n+  def testNewStructFieldWithInvalidMinVersion(self):\n+    \"\"\"Adding a new field using an existing MinVersion breaks backward-\n+    compatibility.\"\"\"\n+    self.assertNotBackwardCompatible(\n+        \"\"\"\\\n+        struct S {\n+          string a;\n+          [MinVersion=1] string? b;\n+        };\n+        \"\"\", \"\"\"\\\n+        struct S {\n+          string a;\n+          [MinVersion=1] string? b;\n+          [MinVersion=1] string? c;\n+        };\"\"\")\n+\n+  def testNewStructFieldWithValidMinVersion(self):\n+    \"\"\"Adding a new field is safe if tagged with a MinVersion greater than any\n+    previously used MinVersion in the struct.\"\"\"\n+    self.assertBackwardCompatible(\n+        'struct S { int32 a; };',\n+        'struct S { int32 a; [MinVersion=1] int32 b; };')\n+    self.assertBackwardCompatible(\n+        'struct S { int32 a; [MinVersion=1] int32 b; };',\n+        'struct S { int32 a; [MinVersion=1] int32 b; [MinVersion=2] bool c; };')\n+\n+  def testNewStructFieldNullableReference(self):\n+    \"\"\"Adding a new nullable reference-typed field is fine if versioned\n+    properly.\"\"\"\n+    self.assertBackwardCompatible(\n+        'struct S { int32 a; };',\n+        'struct S { int32 a; [MinVersion=1] string? b; };')\n+\n+  def testStructFieldRename(self):\n+    \"\"\"Renaming a field has no effect on backward-compatibility.\"\"\"\n+    self.assertBackwardCompatible('struct S { int32 x; bool b; };',\n+                                  'struct S { int32 a; bool b; };')\n+\n+  def testStructFieldReorderWithExplicitOrdinals(self):\n+    \"\"\"Reordering fields has no effect on backward-compatibility when field\n+    ordinals are explicitly labeled and remain unchanged.\"\"\"\n+    self.assertBackwardCompatible('struct S { bool b@1; int32 a@0; };',\n+                                  'struct S { int32 a@0; bool b@1; };')\n+\n+  def testNewUnionFieldUnversioned(self):\n+    \"\"\"Adding a new field to a union without a new (i.e. higher than any\n+    existing version) [MinVersion] tag breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('union U { string a; };',\n+                                     'union U { string a; string b; };')\n+\n+  def testUnionFieldRemoval(self):\n+    \"\"\"Removing a field from a union breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('union U { string a; string b; };',\n+                                     'union U { string a; };')\n+\n+  def testUnionFieldTypeChange(self):\n+    \"\"\"Changing the type of an existing field always breaks\n+    backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('union U { string a; };',\n+                                     'union U { array<int32> a; };')\n+\n+  def testUnionFieldBecomingOptional(self):\n+    \"\"\"Changing a field from non-optional to optional breaks\n+    backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('union U { string a; };',\n+                                     'union U { string? a; };')\n+\n+  def testUnionFieldBecomingNonOptional(self):\n+    \"\"\"Changing a field from optional to non-optional breaks\n+    backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('union U { string? a; };',\n+                                     'union U { string a; };')\n+\n+  def testUnionFieldOrderChange(self):\n+    \"\"\"Changing the order of fields breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('union U { string a; bool b; };',\n+                                     'union U { bool b; string a; };')\n+    self.assertNotBackwardCompatible('union U { string a@0; bool b@1; };',\n+                                     'union U { string a@1; bool b@0; };')\n+\n+  def testUnionFieldMinVersionChange(self):\n+    \"\"\"Changing the MinVersion of a field breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible(\n+        'union U { string a; [MinVersion=1] string b; };',\n+        'union U { string a; [MinVersion=2] string b; };')\n+\n+  def testUnionFieldTypeChange(self):\n+    \"\"\"If a union field's own type definition changes, the containing union\n+    is backward-compatible if and only if the field type's change is\n+    backward-compatible.\"\"\"\n+    self.assertBackwardCompatible(\n+        'struct S {}; union U { S s; };',\n+        'struct S { [MinVersion=1] int32 x; }; union U { S s; };')\n+    self.assertBackwardCompatible(\n+        '[Extensible] enum E { kA }; union U { E e; };',\n+        '[Extensible] enum E { kA, [MinVersion=1] kB }; union U { E e; };')\n+    self.assertNotBackwardCompatible(\n+        'struct S {}; union U { S s; };',\n+        'struct S { int32 x; }; union U { S s; };')\n+    self.assertNotBackwardCompatible(\n+        '[Extensible] enum E { kA }; union U { E e; };',\n+        '[Extensible] enum E { kA, kB }; union U { E e; };')\n+\n+  def testNewUnionFieldWithInvalidMinVersion(self):\n+    \"\"\"Adding a new field using an existing MinVersion breaks backward-\n+    compatibility.\"\"\"\n+    self.assertNotBackwardCompatible(\n+        \"\"\"\\\n+        union U {\n+          string a;\n+          [MinVersion=1] string b;\n+        };\n+        \"\"\", \"\"\"\\\n+        union U {\n+          string a;\n+          [MinVersion=1] string b;\n+          [MinVersion=1] string c;\n+        };\"\"\")\n+\n+  def testNewUnionFieldWithValidMinVersion(self):\n+    \"\"\"Adding a new field is safe if tagged with a MinVersion greater than any\n+    previously used MinVersion in the union.\"\"\"\n+    self.assertBackwardCompatible(\n+        'union U { int32 a; };',\n+        'union U { int32 a; [MinVersion=1] int32 b; };')\n+    self.assertBackwardCompatible(\n+        'union U { int32 a; [MinVersion=1] int32 b; };',\n+        'union U { int32 a; [MinVersion=1] int32 b; [MinVersion=2] bool c; };')\n+\n+  def testUnionFieldRename(self):\n+    \"\"\"Renaming a field has no effect on backward-compatibility.\"\"\"\n+    self.assertBackwardCompatible('union U { int32 x; bool b; };',\n+                                  'union U { int32 a; bool b; };')\n+\n+  def testUnionFieldReorderWithExplicitOrdinals(self):\n+    \"\"\"Reordering fields has no effect on backward-compatibility when field\n+    ordinals are explicitly labeled and remain unchanged.\"\"\"\n+    self.assertBackwardCompatible('union U { bool b@1; int32 a@0; };',\n+                                  'union U { int32 a@0; bool b@1; };')\n+\n+  def testNewInterfaceMethodUnversioned(self):\n+    \"\"\"Adding a new method to an interface without a new (i.e. higher than any\n+    existing version) [MinVersion] tag breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('interface F { A(); };',\n+                                     'interface F { A(); B(); };')\n+\n+  def testInterfaceMethodRemoval(self):\n+    \"\"\"Removing a method from an interface breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('interface F { A(); B(); };',\n+                                     'interface F { A(); };')\n+\n+  def testInterfaceMethodParamsChanged(self):\n+    \"\"\"Changes to the parameter list are only backward-compatible if they meet\n+    backward-compatibility requirements of an equivalent struct definition.\"\"\"\n+    self.assertNotBackwardCompatible('interface F { A(); };',\n+                                     'interface F { A(int32 x); };')\n+    self.assertNotBackwardCompatible('interface F { A(int32 x); };',\n+                                     'interface F { A(bool x); };')\n+    self.assertNotBackwardCompatible(\n+        'interface F { A(int32 x, [MinVersion=1] string? s); };', \"\"\"\\\n+        interface F {\n+          A(int32 x, [MinVersion=1] string? s, [MinVersion=1] int32 y);\n+        };\"\"\")\n+\n+    self.assertBackwardCompatible('interface F { A(int32 x); };',\n+                                  'interface F { A(int32 a); };')\n+    self.assertBackwardCompatible(\n+        'interface F { A(int32 x); };',\n+        'interface F { A(int32 x, [MinVersion=1] string? s); };')\n+\n+    self.assertBackwardCompatible(\n+        'struct S {}; interface F { A(S s); };',\n+        'struct S { [MinVersion=1] int32 x; }; interface F { A(S s); };')\n+    self.assertBackwardCompatible(\n+        'struct S {}; struct T {}; interface F { A(S s); };',\n+        'struct S {}; struct T {}; interface F { A(T s); };')\n+    self.assertNotBackwardCompatible(\n+        'struct S {}; struct T { int32 x; }; interface F { A(S s); };',\n+        'struct S {}; struct T { int32 x; }; interface F { A(T t); };')\n+\n+  def testInterfaceMethodReplyAdded(self):\n+    \"\"\"Adding a reply to a message breaks backward-compatibilty.\"\"\"\n+    self.assertNotBackwardCompatible('interface F { A(); };',\n+                                     'interface F { A() => (); };')\n+\n+  def testInterfaceMethodReplyRemoved(self):\n+    \"\"\"Removing a reply from a message breaks backward-compatibility.\"\"\"\n+    self.assertNotBackwardCompatible('interface F { A() => (); };',\n+                                     'interface F { A(); };')\n+\n+  def testInterfaceMethodReplyParamsChanged(self):\n+    \"\"\"Similar to request parameters, a change to reply parameters is considered\n+    backward-compatible if it meets the same backward-compatibility\n+    requirements imposed on equivalent struct changes.\"\"\"\n+    self.assertNotBackwardCompatible('interface F { A() => (); };',\n+                                     'interface F { A() => (int32 x); };')\n+    self.assertNotBackwardCompatible('interface F { A() => (int32 x); };',\n+                                     'interface F { A() => (); };')\n+    self.assertNotBackwardCompatible('interface F { A() => (bool x); };',\n+                                     'interface F { A() => (int32 x); };')\n+\n+    self.assertBackwardCompatible('interface F { A() => (int32 a); };',\n+                                  'interface F { A() => (int32 x); };')\n+    self.assertBackwardCompatible(\n+        'interface F { A() => (int32 x); };',\n+        'interface F { A() => (int32 x, [MinVersion] string? s); };')\n+\n+  def testNewInterfaceMethodWithInvalidMinVersion(self):\n+    \"\"\"Adding a new method to an existing version is not backward-compatible.\"\"\"\n+    self.assertNotBackwardCompatible(\n+        \"\"\"\\\n+        interface F {\n+          A();\n+          [MinVersion=1] B();\n+        };\n+        \"\"\", \"\"\"\\\n+        interface F {\n+          A();\n+          [MinVersion=1] B();\n+          [MinVersion=1] C();\n+        };\n+        \"\"\")\n+\n+  def testNewInterfaceMethodWithValidMinVersion(self):\n+    \"\"\"Adding a new method is fine as long as its MinVersion exceeds that of any\n+    method on the old interface definition.\"\"\"\n+    self.assertBackwardCompatible('interface F { A(); };',\n+                                  'interface F { A(); [MinVersion=1] B(); };')\ndiff --git a/utils/ipc/mojo/public/tools/run_all_python_unittests.py b/utils/ipc/mojo/public/tools/run_all_python_unittests.py\nnew file mode 100755\nindex 00000000..b2010958\n--- /dev/null\n+++ b/utils/ipc/mojo/public/tools/run_all_python_unittests.py\n@@ -0,0 +1,28 @@\n+#!/usr/bin/env python\n+# Copyright 2020 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+import os.path\n+import sys\n+\n+_TOOLS_DIR = os.path.dirname(__file__)\n+_MOJOM_DIR = os.path.join(_TOOLS_DIR, 'mojom')\n+_SRC_DIR = os.path.join(_TOOLS_DIR, os.path.pardir, os.path.pardir,\n+                        os.path.pardir)\n+\n+# Ensure that the mojom library is discoverable.\n+sys.path.append(_MOJOM_DIR)\n+\n+# Help Python find typ in //third_party/catapult/third_party/typ/\n+sys.path.append(\n+    os.path.join(_SRC_DIR, 'third_party', 'catapult', 'third_party', 'typ'))\n+import typ\n+\n+\n+def Main():\n+  return typ.main(top_level_dir=_MOJOM_DIR)\n+\n+\n+if __name__ == '__main__':\n+  sys.exit(Main())\ndiff --git a/utils/ipc/tools/diagnosis/crbug_1001171.py b/utils/ipc/tools/diagnosis/crbug_1001171.py\nnew file mode 100644\nindex 00000000..478fb8c1\n--- /dev/null\n+++ b/utils/ipc/tools/diagnosis/crbug_1001171.py\n@@ -0,0 +1,51 @@\n+# Copyright 2019 The Chromium Authors. All rights reserved.\n+# Use of this source code is governed by a BSD-style license that can be\n+# found in the LICENSE file.\n+\n+\"\"\"Helper context wrapper for diagnosing crbug.com/1001171.\n+\n+This module and all uses thereof can and should be removed once\n+crbug.com/1001171 has been resolved.\n+\"\"\"\n+\n+from __future__ import print_function\n+\n+import contextlib\n+import os\n+import sys\n+\n+\n+@contextlib.contextmanager\n+def DumpStateOnLookupError():\n+  \"\"\"Prints potentially useful state info in the event of a LookupError.\"\"\"\n+  try:\n+    yield\n+  except LookupError:\n+    print('LookupError diagnosis for crbug.com/1001171:')\n+    for path_index, path_entry in enumerate(sys.path):\n+      desc = 'unknown'\n+      if not os.path.exists(path_entry):\n+        desc = 'missing'\n+      elif os.path.islink(path_entry):\n+        desc = 'link -> %s' % os.path.realpath(path_entry)\n+      elif os.path.isfile(path_entry):\n+        desc = 'file'\n+      elif os.path.isdir(path_entry):\n+        desc = 'dir'\n+      print('  sys.path[%d]: %s (%s)' % (path_index, path_entry, desc))\n+\n+      real_path_entry = os.path.realpath(path_entry)\n+      if (path_entry.endswith(os.path.join('lib', 'python2.7'))\n+          and os.path.isdir(real_path_entry)):\n+        encodings_dir = os.path.realpath(\n+            os.path.join(real_path_entry, 'encodings'))\n+        if os.path.exists(encodings_dir):\n+          if os.path.isdir(encodings_dir):\n+            print('    %s contents: %s' % (encodings_dir,\n+                                           str(os.listdir(encodings_dir))))\n+          else:\n+            print('    %s exists but is not a directory' % encodings_dir)\n+        else:\n+          print('    %s missing' % encodings_dir)\n+\n+    raise\n","prefixes":["libcamera-devel","02/23"]}