Show a patch.

GET /api/patches/12417/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 12417,
    "url": "https://patchwork.libcamera.org/api/patches/12417/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/12417/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20210526033101.1251279-2-paul.elder@ideasonboard.com>",
    "date": "2021-05-26T03:31:01",
    "name": "[libcamera-devel,v3,2/2] utils: ipc: Update mojo",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "21b31ddbf2fb27651b4be064ec91d4311aa30ca8",
    "submitter": {
        "id": 17,
        "url": "https://patchwork.libcamera.org/api/people/17/?format=api",
        "name": "Paul Elder",
        "email": "paul.elder@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/12417/mbox/",
    "series": [
        {
            "id": 2068,
            "url": "https://patchwork.libcamera.org/api/series/2068/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2068",
            "date": "2021-05-26T03:31:00",
            "name": "[libcamera-devel,v3,1/2] utils: update-mojo.sh: Add script for updating mojo",
            "version": 3,
            "mbox": "https://patchwork.libcamera.org/series/2068/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/12417/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/12417/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 CD5E8C3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 26 May 2021 03:31:19 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8D47068926;\n\tWed, 26 May 2021 05:31:19 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id F0039602AB\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 26 May 2021 05:31:17 +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 343078A4;\n\tWed, 26 May 2021 05:31:14 +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=\"GsTaa3sc\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1621999877;\n\tbh=hSAydpnCJUHJfKjz3L2Wpj9sZE4/x2ZGfFv4GYphtg8=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=GsTaa3sc2YPWnKwzcYn1PgD1ppwKe3oWsjTyXcBttiMzAhx4ZBeJueZWKeE5i76Aj\n\tPCLFg2OjUawpWu3A14ACd790QJT24IsWEzhgUmPUOrTBpmVsLvUHTNjK3Rd/kDd8K7\n\tle5CiiedmedPErf8qwmPN76qWM7b0dzMTN6c18Lg=",
        "From": "Paul Elder <paul.elder@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Wed, 26 May 2021 12:31:01 +0900",
        "Message-Id": "<20210526033101.1251279-2-paul.elder@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.27.0",
        "In-Reply-To": "<20210526033101.1251279-1-paul.elder@ideasonboard.com>",
        "References": "<20210526033101.1251279-1-paul.elder@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v3 2/2] utils: ipc: Update 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>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Update mojo from the Chromium repository. The commit from which this was\ntaken is:\n\n9c138d992bfc1fb8f4f7bcf58d00bf19c219e4e2 \"Updating trunk VERSION from\n4523.0 to 4524.0\"\n\nThe update-mojo.sh script was used for this update.\n\nBug: https://bugs.libcamera.org/show_bug.cgi?id=34\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n---\nChanges in v3:\n- the fix for jinja2 3.0.0 has been merged upstream, and is thus in this\n  patch as well (no actual change compared to v2 though, as\n  update-mojo.sh already patched it anyway)\n---\n utils/ipc/mojo/README                         |   4 +\n utils/ipc/mojo/public/tools/BUILD.gn          |   8 +-\n utils/ipc/mojo/public/tools/bindings/BUILD.gn |   9 +-\n .../ipc/mojo/public/tools/bindings/README.md  | 187 +++++--\n .../tools/bindings/gen_data_files_list.py     |   8 +-\n .../tools/bindings/generate_type_mappings.py  |  60 +-\n .../ipc/mojo/public/tools/bindings/mojom.gni  | 526 +++++++++++-------\n utils/ipc/mojo/public/tools/mojom/README.md   |   2 +-\n .../mojom/check_stable_mojom_compatibility.py |   3 +-\n .../mojo/public/tools/mojom/mojom/BUILD.gn    |   1 -\n .../tools/mojom/mojom/generate/generator.py   |  14 +-\n .../tools/mojom/mojom/generate/module.py      | 163 +++++-\n .../mojom/mojom/generate/template_expander.py |  11 +-\n .../tools/mojom/mojom/generate/translate.py   |  26 +-\n .../mojo/public/tools/mojom/mojom_parser.py   | 210 +++++--\n .../mojom/version_compatibility_unittest.py   |  46 +-\n utils/ipc/tools/README                        |   4 +\n 17 files changed, 852 insertions(+), 430 deletions(-)\n create mode 100644 utils/ipc/mojo/README\n create mode 100644 utils/ipc/tools/README",
    "diff": "diff --git a/utils/ipc/mojo/README b/utils/ipc/mojo/README\nnew file mode 100644\nindex 00000000..d5c24fc3\n--- /dev/null\n+++ b/utils/ipc/mojo/README\n@@ -0,0 +1,4 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+Files in this directory are imported from 9c138d992bfc of Chromium. Do not\n+modify them manually.\ndiff --git a/utils/ipc/mojo/public/tools/BUILD.gn b/utils/ipc/mojo/public/tools/BUILD.gn\nindex 4c68350b..eb6391a6 100644\n--- a/utils/ipc/mojo/public/tools/BUILD.gn\n+++ b/utils/ipc/mojo/public/tools/BUILD.gn\n@@ -8,11 +8,11 @@\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+  data_deps = [\n+    \"//testing:test_scripts_shared\",\n+    \"//third_party/catapult/third_party/typ/\",\n+  ]\n }\ndiff --git a/utils/ipc/mojo/public/tools/bindings/BUILD.gn b/utils/ipc/mojo/public/tools/bindings/BUILD.gn\nindex 8ba6e922..3e242532 100644\n--- a/utils/ipc/mojo/public/tools/bindings/BUILD.gn\n+++ b/utils/ipc/mojo/public/tools/bindings/BUILD.gn\n@@ -2,10 +2,12 @@\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/python.gni\")\n import(\"//mojo/public/tools/bindings/mojom.gni\")\n import(\"//third_party/jinja2/jinja2.gni\")\n \n-action(\"precompile_templates\") {\n+# TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+python2_action(\"precompile_templates\") {\n   sources = mojom_generator_sources\n   sources += [\n     \"$mojom_generator_root/generators/cpp_templates/enum_macros.tmpl\",\n@@ -69,11 +71,16 @@ action(\"precompile_templates\") {\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/enum_definition_for_module.tmpl\",\n     \"$mojom_generator_root/generators/js_templates/lite/interface_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/interface_definition_for_module.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/mojom.m.js.tmpl\",\n     \"$mojom_generator_root/generators/js_templates/lite/struct_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/struct_definition_for_module.tmpl\",\n     \"$mojom_generator_root/generators/js_templates/lite/union_definition.tmpl\",\n+    \"$mojom_generator_root/generators/js_templates/lite/union_definition_for_module.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\",\ndiff --git a/utils/ipc/mojo/public/tools/bindings/README.md b/utils/ipc/mojo/public/tools/bindings/README.md\nindex 1a3d5c58..43882450 100644\n--- a/utils/ipc/mojo/public/tools/bindings/README.md\n+++ b/utils/ipc/mojo/public/tools/bindings/README.md\n@@ -113,8 +113,8 @@ for message parameters.\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+This is used strictly for aggregating all defined symbols therein within a\n+common Mojom namespace. The specific impact this has on generated bindings code\n varies for each target language. For example, if the following Mojom is used to\n generate bindings:\n \n@@ -132,7 +132,7 @@ Generated C++ bindings will define a class interface `MoneyGenerator` in the\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+declare a module name with at least (and preferably exactly) one top-level name\n as well as an inner `mojom` module suffix. *e.g.*, `chrome.mojom`,\n `business.mojom`, *etc.*\n \n@@ -271,7 +271,7 @@ code, see\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+collection of fields which may take 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@@ -320,7 +320,7 @@ 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+target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages).\n \n ### Constants\n \n@@ -346,7 +346,7 @@ struct Employee {\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+target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages).\n \n ### Interfaces\n \n@@ -379,58 +379,82 @@ 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+* **`[Sync]`**:\n+  The `Sync` attribute may be specified for any interface method which expects a\n+  response. This makes it so that callers of the method can wait synchronously\n+  for a response. See [Synchronous\n+  Calls](/mojo/public/cpp/bindings/README.md#Synchronous-Calls) in the C++\n+  bindings documentation. Note that sync methods are only actually synchronous\n+  when called from C++.\n+\n+* **`[NoInterrupt]`**:\n+  When a thread is waiting for a reply to a `Sync` message, it's possible to be\n+  woken up to dispatch other unrelated incoming `Sync` messages. This measure\n+  helps to avoid deadlocks. If a `Sync` message is also marked as `NoInterrupt`\n+  however, this behavior is disabled: instead the calling thread will only wake\n+  up for the precise message being waited upon. This attribute must be used with\n+  extreme caution, because it can lead to deadlocks otherwise.\n+\n+* **`[Default]`**:\n+  The `Default` attribute may be used to specify an enumerator value that\n+  will be used if an `Extensible` enumeration does not deserialize to a known\n+  value on the receiver side, i.e. the sender is using a newer version of the\n+  enum. This allows unknown values to be mapped to a well-defined value that can\n+  be appropriately handled.\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+  Note: in the future, an `Extensible` enumeration will require that a `Default`\n+  enumerator value also be specified.\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. See [Repurposing Legacy IPC\n+  Traits](/docs/mojo_ipc_conversion.md#repurposing-and-invocations) for more\n+  details. Note support for this attribute is strictly limited to C++ bindings\n+  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+* **`[Uuid=<UUID>]`**:\n+  Specifies a UUID to be associated with a given interface. The UUID is intended\n+  to remain stable across all changes to the interface definition, including\n+  name changes. The value given for this attribute should be a standard UUID\n+  string representation as specified by RFC 4122. New UUIDs can be generated\n+  with common tools such as `uuidgen`.\n+\n+* **`[EnableIf=value]`**:\n+  The `EnableIf` attribute is used to conditionally enable definitions when the\n+  mojom is parsed. If the `mojom` target in the GN file does not include the\n+  matching `value` in the list of `enabled_features`, the definition will be\n+  disabled. This is useful for mojom definitions that only make sense on one\n+  platform. Note that the `EnableIf` attribute can only be set once per\n+  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+Mojom concepts translate to a given target language, please refer to the\n bindings API documentation for that language:\n \n * [C++ Bindings](/mojo/public/cpp/bindings/README.md)\n@@ -441,7 +465,7 @@ bindings API documentation for that language:\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+interface. This helps to ensure consistent validation across interfaces without\n leaving the burden to developers and security reviewers every time a new message\n is added.\n \n@@ -555,25 +579,37 @@ struct Employee {\n };\n ```\n \n-and you would like to add a birthday field. You can do:\n+and you would like to add birthday and nickname fields. You can add them as\n+optional types with a `MinVersion` like so:\n \n ``` cpp\n struct Employee {\n   uint64 employee_id;\n   string name;\n   [MinVersion=1] Date? birthday;\n+  [MinVersion=1] string? nickname;\n };\n ```\n \n+*** note\n+**NOTE:** Mojo object or handle types added with a `MinVersion` **MUST** be\n+optional (nullable). See [Primitive Types](#Primitive-Types) for details on\n+nullable values.\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+The value of `MinVersion` is unrelated to ordinals. The choice of a particular\n+version number is arbitrary. All its usage means is that a field isn't present\n+before the numbered version.\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+deprecation clear and add a new field with a new `MinVersion` number.\n ***\n \n **Ordinal value** refers to the relative positional layout of a struct's fields\n@@ -602,14 +638,10 @@ struct Employee {\n   uint64 employee_id@0;\n   [MinVersion=1] Date? birthday@2;\n   string name@1;\n+  [MinVersion=1] string? nickname@3;\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@@ -706,6 +738,39 @@ 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+### Renaming versioned structs\n+It's possible to rename versioned structs by using the `[RenamedFrom]` attribute.\n+RenamedFrom\n+\n+``` cpp\n+module asdf.mojom;\n+\n+// Old version:\n+[Stable]\n+struct OldStruct {\n+};\n+\n+// New version:\n+[Stable, RenamedFrom=\"asdf.mojom.OldStruct\"]\n+struct NewStruct {\n+};\n+```\n+\n+## Component targets\n+\n+If there are multiple components depending on the same mojom target within one binary,\n+the target will need to be defined as `mojom_component` instead of `mojom`.\n+Since `mojom` targets are generated `source_set` targets and `mojom_component` targets\n+are generated `component` targets, you would use `mojom_component` in the same cases\n+where you would use `component` for non-mojom files.\n+*** note\n+**NOTE**: by default, components for both blink and non-blink bindings are generated.\n+Use the `disable_variants` target parameter to generate only non-blink bindings.\n+You can also generate a `source_set` for one of the variants by defining\n+[export_*](https://source.chromium.org/chromium/chromium/src/+/main:mojo/public/tools/bindings/mojom.gni;drc=739b9fbce50310c1dd2b59c279cd90a9319cb6e8;l=318)\n+parameters for the `mojom_component` target.\n+***\n+\n ## Grammar Reference\n \n Below is the (BNF-ish) context-free grammar of the Mojom language:\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\nindex 79c9e50e..8b78d092 100644\n--- a/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py\n+++ b/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py\n@@ -18,7 +18,6 @@ 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@@ -41,12 +40,9 @@ def main():\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+  contents = '\\n'.join(f for f in files) + '\\n'\n+  WriteFile(contents, options.output)\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\nindex 64ca048f..a0096649 100755\n--- a/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py\n+++ b/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py\n@@ -75,14 +75,6 @@ def ReadTypemap(path):\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@@ -102,52 +94,6 @@ def LoadCppTypemapConfig(path):\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@@ -170,10 +116,10 @@ def main():\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+  params, _ = parser.parse_known_args()\n+  typemaps = {}\n   if params.cpp_config_path:\n-    typemaps.update(LoadCppTypemapConfig(params.cpp_config_path))\n+    typemaps = 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))\ndiff --git a/utils/ipc/mojo/public/tools/bindings/mojom.gni b/utils/ipc/mojo/public/tools/bindings/mojom.gni\nindex a739fa6e..fe2a1da3 100644\n--- a/utils/ipc/mojo/public/tools/bindings/mojom.gni\n+++ b/utils/ipc/mojo/public/tools/bindings/mojom.gni\n@@ -2,7 +2,7 @@\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(\"//build/config/python.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@@ -64,10 +64,13 @@ declare_args() {\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+#\n+# TODO(crbug.com/1052397): Remove !chromeos_is_browser_only once\n+# lacros-chrome switches to target_os=\"chromeos\"\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+    (is_mac || is_win ||\n+     (is_linux && !is_chromeos_ash && !is_chromecast && !is_chromeos_lacros) ||\n      ((enable_nacl || is_nacl || is_nacl_nonsfi) &&\n       (target_os != \"chromeos\" && !chromeos_is_browser_only)))\n \n@@ -78,7 +81,6 @@ 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@@ -94,6 +96,7 @@ 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/cpp_util.py\",\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@@ -107,17 +110,6 @@ if (enable_scrambled_message_ids) {\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@@ -126,56 +118,11 @@ if (enable_scrambled_message_ids) {\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@@ -218,6 +165,15 @@ if (!is_ios) {\n #   import_dirs (optional)\n #       List of import directories that will get added when processing sources.\n #\n+#   input_root_override (optional)\n+#       Root path for the .mojom files used to generate the namespaces for\n+#       interfaces. Useful with targets outside //, e.g. in parent directories\n+#       above \"//\". The default input root is //\n+#       Example: Vivaldi's source root is \"//vivaldi/\",\n+#       and \"//vivaldi/chromium/\" is \"//\"\n+#       In such cases, not using this argument lead to the output files being\n+#       located in different directories than expected.\n+#\n #   testonly (optional)\n #\n #   visibility (optional)\n@@ -245,6 +201,43 @@ if (!is_ios) {\n #   blink_cpp_typemaps (optional)\n #       Same as above, but for the Blink variant of generated C++ bindings.\n #\n+#   cpp_proxy_target (optional)\n+#       The name of a target which all C++ dependencies will link against\n+#       instead of linking directly against this mojom target's generated C++\n+#       sources. Normally when declaring invoking the mojom(\"foo\") target, GN\n+#       emits a source_set or component target named \"foo\" which encompasses the\n+#       default variant of generated C++ bindings. This changes that to instead\n+#       emit a group(\"foo\") which merely forwards public_deps to the named\n+#       `cpp_proxy_target`. That target must in turn depend on\n+#       \"foo_cpp_sources\".\n+#\n+#       This is useful primarily in conjunction with export_define et al to\n+#       embed generated C++ bindings within an existing component target.\n+#\n+#   blink_cpp_proxy_target (optional)\n+#       Same concept as `cpp_proxy_target` above, but affects the generated\n+#       \"foo_blink\" Blink-variant C++ bindings.\n+#\n+#   cpp_configs (optional)\n+#       A list of extra configs to apply to the default variant of generated C++\n+#       bindings.\n+#\n+#   blink_cpp_configs (optional)\n+#       A list of extra configs to apply to the Blink variant of generated C++\n+#       bindings.\n+#\n+#   mojom_source_deps (optional)\n+#       A list of mojoms this target depends upon. This is equivalent to\n+#       public_deps except that the C++ bindings depend on each of the named\n+#       \"foo\" targets' \"foo_cpp_sources\" rather than on foo's\n+#       `cpp_proxy_target`. It only makes sense to use this for dependencies\n+#       that set `cpp_proxy_target`, and only when the dependent mojom() would\n+#       otherwise have circular dependencies with that proxy target.\n+#\n+#   mojom_blink_source_deps (optional)\n+#       Same as above but depends on \"foo_blink_cpp_sources\" and is used for\n+#       dependencies that specify a `blink_cpp_proxy_target`.\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@@ -327,6 +320,21 @@ if (!is_ios) {\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+#   webui_module_path (optional)\n+#       The path or URL at which modules generated by this target will be\n+#       accessible to WebUI pages. This may either be an absolute path or\n+#       a full URL path starting with \"chrome://resources/mojo\".\n+#\n+#       If an absolute path, a WebUI page may only import these modules if\n+#       they are manually packaged and mapped independently by that page's\n+#       WebUIDataSource. The mapped path must match the path given here.\n+#\n+#       If this is is instead a URL string starting with\n+#       \"chrome://resources/mojo\", the generated resources must be added to\n+#       content_resources.grd and registered with\n+#       content::SharedResourcesDataSource with a corresponding path, at which\n+#       point they will be made available to all WebUI pages at the given URL.\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@@ -410,8 +418,8 @@ if (!is_ios) {\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+#             type with absl::optional, and null values are simply\n+#             absl::nullopt.\n #\n #         hashable (optional)\n #             A boolean value (default false) indicating whether the C++ type is\n@@ -463,14 +471,15 @@ template(\"mojom\") {\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_define) || defined(invoker.cpp_configs))\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_define_blink) ||\n+           defined(invoker.blink_cpp_configs))\n     assert(defined(invoker.export_header_blink))\n \n     # Not all platforms use the Blink variant, so make sure GN doesn't complain\n@@ -506,12 +515,22 @@ template(\"mojom\") {\n       !invoker.disallow_interfaces\n \n   all_deps = []\n+  mojom_cpp_deps = []\n   if (defined(invoker.deps)) {\n     all_deps += invoker.deps\n+    mojom_cpp_deps += invoker.deps\n   }\n   if (defined(invoker.public_deps)) {\n     all_deps += invoker.public_deps\n+    mojom_cpp_deps += invoker.public_deps\n+  }\n+  if (defined(invoker.mojom_source_deps)) {\n+    all_deps += invoker.mojom_source_deps\n   }\n+  if (defined(invoker.mojom_blink_source_deps)) {\n+    all_deps += invoker.mojom_blink_source_deps\n+  }\n+  not_needed([ \"mojom_deps\" ])\n \n   if (defined(invoker.component_macro_prefix)) {\n     assert(defined(invoker.component_output_prefix))\n@@ -536,12 +555,6 @@ template(\"mojom\") {\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@@ -571,7 +584,11 @@ template(\"mojom\") {\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+  if (defined(invoker.input_root_override)) {\n+    source_abspaths = rebase_path(sources_list, invoker.input_root_override)\n+  } else {\n+    source_abspaths = rebase_path(sources_list, \"//\")\n+  }\n   output_file_base_paths = []\n   foreach(path, source_abspaths) {\n     output_file_base_paths +=\n@@ -643,21 +660,31 @@ template(\"mojom\") {\n     }\n     if (is_android) {\n       enabled_features += [ \"is_android\" ]\n-    } else if (is_chromeos) {\n-      enabled_features += [ \"is_chromeos\" ]\n+    } else if (is_chromeos_ash) {\n+      enabled_features += [\n+        \"is_chromeos\",\n+        \"is_chromeos_ash\",\n+      ]\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+    } else if (is_linux || is_chromeos_lacros) {\n       enabled_features += [ \"is_linux\" ]\n+      if (is_chromeos_lacros) {\n+        enabled_features += [\n+          \"is_chromeos\",\n+          \"is_chromeos_lacros\",\n+        ]\n+      }\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+    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+    python2_action(parser_target_name) {\n       script = mojom_parser_script\n       inputs = mojom_parser_sources + [ build_metadata_filename ]\n       sources = sources_list\n@@ -679,7 +706,7 @@ template(\"mojom\") {\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+        rebase_path(\"//.\"),\n         \"--input-root\",\n         rebase_path(root_gen_dir),\n \n@@ -692,12 +719,26 @@ template(\"mojom\") {\n         rebase_path(build_metadata_filename),\n       ]\n \n+      if (defined(invoker.input_root_override)) {\n+        args += [\n+          \"--input-root\",\n+          rebase_path(invoker.input_root_override),\n+        ]\n+      }\n+\n       foreach(enabled_feature, enabled_features) {\n         args += [\n           \"--enable-feature\",\n           enabled_feature,\n         ]\n       }\n+\n+      if (defined(invoker.webui_module_path)) {\n+        args += [\n+          \"--add-module-metadata\",\n+          \"webui_module_path=${invoker.webui_module_path}\",\n+        ]\n+      }\n     }\n   }\n \n@@ -705,18 +746,29 @@ template(\"mojom\") {\n \n   # Generate code that is shared by different variants.\n   if (sources_list != []) {\n+    base_dir = \"//\"\n+    if (defined(invoker.input_root_override)) {\n+      base_dir = invoker.input_root_override\n+    }\n+\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+      rebase_path(base_dir, 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+    if (defined(invoker.input_root_override)) {\n+      common_generator_args += [\n+        \"-I\",\n+        rebase_path(invoker.input_root_override, root_build_dir),\n+      ]\n+    }\n \n     if (defined(invoker.disallow_native_types) &&\n         invoker.disallow_native_types) {\n@@ -767,7 +819,8 @@ template(\"mojom\") {\n       }\n     }\n \n-    action(generator_cpp_message_ids_target_name) {\n+    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+    python2_action(generator_cpp_message_ids_target_name) {\n       script = mojom_generator_script\n       inputs = mojom_generator_sources + jinja2_sources\n       sources = sources_list\n@@ -806,7 +859,9 @@ template(\"mojom\") {\n     }\n \n     generator_shared_target_name = \"${target_name}_shared__generator\"\n-    action(generator_shared_target_name) {\n+\n+    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+    python2_action(generator_shared_target_name) {\n       visibility = [ \":*\" ]\n       script = mojom_generator_script\n       inputs = mojom_generator_sources + jinja2_sources\n@@ -864,7 +919,7 @@ template(\"mojom\") {\n   }\n \n   shared_cpp_sources_target_name = \"${target_name}_shared_cpp_sources\"\n-  jumbo_source_set(shared_cpp_sources_target_name) {\n+  source_set(shared_cpp_sources_target_name) {\n     if (defined(invoker.testonly)) {\n       testonly = invoker.testonly\n     }\n@@ -925,7 +980,9 @@ template(\"mojom\") {\n \n     generator_mojolpm_proto_target_name =\n         \"${target_name}_mojolpm_proto_generator\"\n-    action(generator_mojolpm_proto_target_name) {\n+\n+    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+    python2_action(generator_mojolpm_proto_target_name) {\n       script = mojom_generator_script\n       inputs = mojom_generator_sources + jinja2_sources\n       sources = invoker.sources\n@@ -960,11 +1017,10 @@ template(\"mojom\") {\n         sources = process_file_template(\n                 invoker.sources,\n                 [ \"{{source_gen_dir}}/{{source_file_part}}.mojolpm.proto\" ])\n-        import_dirs = [ \"${root_gen_dir}\" ]\n+        import_dirs = [ \"//\" ]\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+        proto_deps = [ \":$generator_mojolpm_proto_target_name\" ]\n         link_deps = [ \"//mojo/public/tools/fuzzers:mojolpm_proto\" ]\n \n         foreach(d, all_deps) {\n@@ -995,12 +1051,22 @@ template(\"mojom\") {\n   }\n \n   # Generate code for variants.\n-  if (!defined(invoker.disable_variants) || !invoker.disable_variants) {\n-    enabled_configurations = _bindings_configurations\n+  default_variant = {\n+    component_macro_suffix = \"\"\n+  }\n+  if ((!defined(invoker.disable_variants) || !invoker.disable_variants) &&\n+      !is_ios) {\n+    blink_variant = {\n+      variant = \"blink\"\n+      component_macro_suffix = \"_BLINK\"\n+      for_blink = true\n+    }\n+    enabled_configurations = [\n+      default_variant,\n+      blink_variant,\n+    ]\n   } else {\n-    first_config = _bindings_configurations[0]\n-    assert(!defined(first_config.variant))\n-    enabled_configurations = [ first_config ]\n+    enabled_configurations = [ default_variant ]\n   }\n   foreach(bindings_configuration, enabled_configurations) {\n     cpp_only = false\n@@ -1018,6 +1084,11 @@ template(\"mojom\") {\n     export_defines = []\n     export_defines_overridden = false\n     force_source_set = false\n+    proxy_target = \"\"\n+    extra_configs = []\n+    output_visibility = []\n+    output_visibility = [ \"*\" ]\n+    cpp_source_deps = []\n     if (defined(bindings_configuration.for_blink) &&\n         bindings_configuration.for_blink) {\n       if (defined(invoker.blink_cpp_typemaps)) {\n@@ -1028,18 +1099,47 @@ template(\"mojom\") {\n         export_defines = [ invoker.export_define_blink ]\n         force_source_set = true\n       }\n+      if (defined(invoker.blink_cpp_configs)) {\n+        extra_configs += invoker.blink_cpp_configs\n+      }\n+      if (defined(invoker.blink_cpp_proxy_target)) {\n+        proxy_target = invoker.blink_cpp_proxy_target\n+      }\n+      if (defined(invoker.visibility_blink)) {\n+        output_visibility = []\n+        output_visibility = invoker.visibility_blink\n+      }\n+      if (defined(invoker.mojom_blink_source_deps)) {\n+        cpp_source_deps = invoker.mojom_blink_source_deps\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+      if (defined(invoker.cpp_configs)) {\n+        extra_configs += invoker.cpp_configs\n+      }\n+      if (defined(invoker.cpp_proxy_target)) {\n+        proxy_target = invoker.cpp_proxy_target\n+      }\n+      if (defined(invoker.visibility)) {\n+        output_visibility = []\n+        output_visibility = invoker.visibility\n+      }\n+      if (defined(invoker.mojom_source_deps)) {\n+        cpp_source_deps = invoker.mojom_source_deps\n+      }\n     }\n     not_needed([ \"cpp_typemap_configs\" ])\n+    if (proxy_target != \"\") {\n+      group(\"${target_name}${variant_suffix}__has_cpp_proxy\") {\n+      }\n+    }\n \n     if (!export_defines_overridden && defined(invoker.component_macro_prefix)) {\n       output_name_override =\n@@ -1089,7 +1189,6 @@ template(\"mojom\") {\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@@ -1104,20 +1203,11 @@ template(\"mojom\") {\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+\n+      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+      python2_action(generator_target_name) {\n         visibility = [ \":*\" ]\n         script = mojom_generator_script\n         inputs = mojom_generator_sources + jinja2_sources\n@@ -1249,6 +1339,7 @@ template(\"mojom\") {\n           # mojolpm only uses the no-variant type.\n           \":$mojolpm_generator_target_name\",\n           \":$mojolpm_proto_target_name\",\n+          \"//base\",\n           \"//mojo/public/tools/fuzzers:mojolpm\",\n         ]\n \n@@ -1260,18 +1351,6 @@ template(\"mojom\") {\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@@ -1309,7 +1388,9 @@ template(\"mojom\") {\n     }\n     write_file(_typemap_config_filename, _rebased_typemap_configs, \"json\")\n     _mojom_target_name = target_name\n-    action(_typemap_validator_target_name) {\n+\n+    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+    python2_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@@ -1320,9 +1401,10 @@ template(\"mojom\") {\n       ]\n     }\n \n-    action(type_mappings_target_name) {\n-      inputs = _bindings_configuration_files + mojom_generator_sources +\n-               jinja2_sources + [ _typemap_stamp_filename ]\n+    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+    python2_action(type_mappings_target_name) {\n+      inputs =\n+          mojom_generator_sources + 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@@ -1349,46 +1431,12 @@ template(\"mojom\") {\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+      # 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     group(\"${target_name}${variant_suffix}_headers\") {\n@@ -1404,32 +1452,45 @@ template(\"mojom\") {\n         full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n         public_deps += [ \"${full_name}${variant_suffix}_headers\" ]\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 \n+    js_data_deps_target_name = target_name + \"_js_data_deps\"\n+    not_needed([ \"js_data_deps_target_name\" ])\n+\n     if (!force_source_set && defined(invoker.component_macro_prefix)) {\n-      output_target_type = \"component\"\n+      sources_target_type = \"component\"\n     } else {\n-      output_target_type = \"source_set\"\n+      sources_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+    output_target_name = \"${target_name}${variant_suffix}\"\n+    if (proxy_target != \"\") {\n+      group(output_target_name) {\n+        public_deps = [ proxy_target ]\n+        visibility = output_visibility\n+        if (defined(invoker.testonly)) {\n+          testonly = invoker.testonly\n+        }\n+      }\n+      sources_target_name = \"${output_target_name}_cpp_sources\"\n+    } else {\n+      sources_target_name = output_target_name\n+    }\n \n-    target(\"jumbo_\" + output_target_type, \"${target_name}${variant_suffix}\") {\n+    target(sources_target_type, sources_target_name) {\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+      visibility = output_visibility + [ \":$output_target_name\" ]\n       if (defined(invoker.testonly)) {\n         testonly = invoker.testonly\n       }\n       defines = export_defines\n+      configs += extra_configs\n       if (output_file_base_paths != []) {\n         sources = []\n         foreach(base_path, output_file_base_paths) {\n@@ -1456,13 +1517,20 @@ template(\"mojom\") {\n       if (sources_list != []) {\n         public_deps += [ \":$generator_target_name\" ]\n       }\n-      foreach(d, all_deps) {\n+      foreach(d, mojom_cpp_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+        full_name = get_label_info(d, \"label_no_toolchain\")\n         public_deps += [ \"${full_name}${variant_suffix}\" ]\n       }\n+      foreach(d, cpp_source_deps) {\n+        full_name = get_label_info(d, \"label_no_toolchain\")\n+        public_deps += [\n+          \"${full_name}${variant_suffix}__has_cpp_proxy\",\n+          \"${full_name}${variant_suffix}_cpp_sources\",\n+        ]\n+      }\n       if (defined(bindings_configuration.for_blink) &&\n           bindings_configuration.for_blink) {\n         if (defined(invoker.overridden_deps_blink)) {\n@@ -1493,20 +1561,6 @@ template(\"mojom\") {\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@@ -1518,18 +1572,10 @@ template(\"mojom\") {\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@@ -1537,7 +1583,8 @@ template(\"mojom\") {\n \n       java_generator_target_name = target_name + \"_java__generator\"\n       if (sources_list != []) {\n-        action(java_generator_target_name) {\n+        # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+        python2_action(java_generator_target_name) {\n           script = mojom_generator_script\n           inputs = mojom_generator_sources + jinja2_sources\n           sources = sources_list\n@@ -1576,7 +1623,9 @@ template(\"mojom\") {\n       }\n \n       java_srcjar_target_name = target_name + \"_java_sources\"\n-      action(java_srcjar_target_name) {\n+\n+      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+      python2_action(java_srcjar_target_name) {\n         script = \"//build/android/gyp/zip.py\"\n         inputs = []\n         if (output_file_base_paths != []) {\n@@ -1605,6 +1654,7 @@ template(\"mojom\") {\n           \"//base:base_java\",\n           \"//mojo/public/java:bindings_java\",\n           \"//mojo/public/java:system_java\",\n+          \"//third_party/androidx:androidx_annotation_annotation_java\",\n         ]\n \n         # Disable warnings/checks on these generated files.\n@@ -1635,7 +1685,9 @@ template(\"mojom\") {\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+\n+      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+      python2_action(generator_js_target_name) {\n         script = mojom_generator_script\n         inputs = mojom_generator_sources + jinja2_sources\n         sources = sources_list\n@@ -1656,10 +1708,15 @@ template(\"mojom\") {\n           outputs += [\n             \"$root_gen_dir/$base_path.js\",\n             \"$root_gen_dir/$base_path.externs.js\",\n+            \"$root_gen_dir/$base_path.m.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+          if (defined(invoker.webui_module_path)) {\n+            outputs += [ \"$root_gen_dir/mojom-webui/$base_path-webui.js\" ]\n+          }\n         }\n \n         response_file_contents = filelist\n@@ -1708,13 +1765,19 @@ template(\"mojom\") {\n         foreach(base_path, output_file_base_paths) {\n           data += [\n             \"$root_gen_dir/${base_path}.js\",\n+            \"$root_gen_dir/${base_path}.m.js\",\n             \"$root_gen_dir/${base_path}-lite.js\",\n           ]\n         }\n         deps += [ \":$generator_js_target_name\" ]\n       }\n \n-      data_deps = []\n+      if (defined(invoker.disallow_native_types) &&\n+          invoker.disallow_native_types) {\n+        data_deps = []\n+      } else {\n+        data_deps = [ \"//mojo/public/js:bindings_module\" ]\n+      }\n       foreach(d, all_deps) {\n         full_name = get_label_info(d, \"label_no_toolchain\")\n         data_deps += [ \"${full_name}_js_data_deps\" ]\n@@ -1770,6 +1833,64 @@ template(\"mojom\") {\n       group(js_library_for_compile_target_name) {\n       }\n     }\n+\n+    js_modules_target_name = \"${target_name}_js_modules\"\n+    if (sources_list != []) {\n+      js_library(js_modules_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}.m.js\" ]\n+        }\n+        externs_list = [\n+          \"${externs_path}/mojo_core.js\",\n+          \"${externs_path}/pending.js\",\n+        ]\n+        if (defined(invoker.disallow_native_types) &&\n+            invoker.disallow_native_types) {\n+          deps = []\n+        } else {\n+          deps = [ \"//mojo/public/js:bindings_uncompiled\" ]\n+        }\n+        foreach(d, all_deps) {\n+          full_name = get_label_info(d, \"label_no_toolchain\")\n+          deps += [ \"${full_name}_js_modules\" ]\n+        }\n+      }\n+    } else {\n+      group(js_modules_target_name) {\n+      }\n+    }\n+\n+    if (defined(invoker.webui_module_path)) {\n+      webui_js_target_name = \"${target_name}_webui_js\"\n+      if (sources_list != []) {\n+        js_library(webui_js_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/mojom-webui/${base_path}-webui.js\" ]\n+          }\n+          externs_list = [\n+            \"${externs_path}/mojo_core.js\",\n+            \"${externs_path}/pending.js\",\n+          ]\n+          if (defined(invoker.disallow_native_types) &&\n+              invoker.disallow_native_types) {\n+            deps = []\n+          } else {\n+            deps = [ \"//mojo/public/js:bindings_uncompiled\" ]\n+          }\n+          foreach(d, all_deps) {\n+            full_name = get_label_info(d, \"label_no_toolchain\")\n+            deps += [ \"${full_name}_webui_js\" ]\n+          }\n+        }\n+      } else {\n+        group(webui_js_target_name) {\n+        }\n+      }\n+    }\n   }\n   if ((generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) &&\n       use_typescript_for_target) {\n@@ -1806,7 +1927,9 @@ template(\"mojom\") {\n       # Generate Typescript bindings.\n       generator_ts_target_name =\n           \"${target_name}_${dependency_type.name}__ts__generator\"\n-      action(generator_ts_target_name) {\n+\n+      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+      python2_action(generator_ts_target_name) {\n         script = mojom_generator_script\n         inputs = mojom_generator_sources + jinja2_sources\n         sources = sources_list\n@@ -1873,7 +1996,8 @@ template(\"mojom\") {\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+      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+      python2_action(generator_js_target_name) {\n         script = \"$mojom_generator_root/compile_typescript.py\"\n         sources = ts_outputs\n         outputs = js_outputs\ndiff --git a/utils/ipc/mojo/public/tools/mojom/README.md b/utils/ipc/mojo/public/tools/mojom/README.md\nindex 6a4ff78a..e5d17ab0 100644\n--- a/utils/ipc/mojo/public/tools/mojom/README.md\n+++ b/utils/ipc/mojo/public/tools/mojom/README.md\n@@ -3,7 +3,7 @@\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+[Mojo IPC library](https://chromium.googlesource.com/chromium/src/+/main/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\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\nindex 7e746112..08bd672f 100755\n--- a/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py\n+++ b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py\n@@ -131,7 +131,8 @@ def _ValidateDelta(root, delta):\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+    checker = module.BackwardCompatibilityChecker()\n+    if not checker.IsBackwardCompatible(new_types[new_name], 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 '\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\nindex 7416ef19..51facc0c 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\n@@ -8,7 +8,6 @@ group(\"mojom\") {\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\",\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\nindex de62260a..4a1c73fc 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\n@@ -136,9 +136,14 @@ class Stylizer(object):\n \n def WriteFile(contents, full_path):\n   # If |contents| is same with the file content, we skip updating.\n+  if not isinstance(contents, bytes):\n+    data = contents.encode('utf8')\n+  else:\n+    data = contents\n+\n   if os.path.isfile(full_path):\n     with open(full_path, 'rb') as destination_file:\n-      if destination_file.read() == contents:\n+      if destination_file.read() == data:\n         return\n \n   # Make sure the containing directory exists.\n@@ -146,11 +151,8 @@ def WriteFile(contents, 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+  with open(full_path, 'wb') as f:\n+    f.write(data)\n \n \n def AddComputedData(module):\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\nindex 8547ff64..9bdb28e0 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\n@@ -12,7 +12,33 @@\n # method = interface.AddMethod('Tat', 0)\n # method.AddParameter('baz', 0, mojom.INT32)\n \n-import pickle\n+import sys\n+if sys.version_info.major == 2:\n+  import cPickle as pickle\n+else:\n+  import pickle\n+from uuid import UUID\n+\n+\n+class BackwardCompatibilityChecker(object):\n+  \"\"\"Used for memoization while recursively checking two type definitions for\n+  backward-compatibility.\"\"\"\n+\n+  def __init__(self):\n+    self._cache = {}\n+\n+  def IsBackwardCompatible(self, new_kind, old_kind):\n+    key = (new_kind, old_kind)\n+    result = self._cache.get(key)\n+    if result is None:\n+      # Assume they're compatible at first to effectively ignore recursive\n+      # checks between these types, e.g. if both kinds are a struct or union\n+      # that references itself in a field.\n+      self._cache[key] = True\n+      result = new_kind.IsBackwardCompatible(old_kind, self)\n+      self._cache[key] = result\n+    return result\n+\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@@ -114,6 +140,10 @@ class Kind(object):\n     # during a subsequent run of the parser.\n     return hash((self.spec, self.parent_kind))\n \n+  # pylint: disable=unused-argument\n+  def IsBackwardCompatible(self, rhs, checker):\n+    return self == rhs\n+\n \n class ReferenceKind(Kind):\n   \"\"\"ReferenceKind represents pointer and handle types.\n@@ -195,6 +225,10 @@ class ReferenceKind(Kind):\n   def __hash__(self):\n     return hash((super(ReferenceKind, self).__hash__(), self.is_nullable))\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return (super(ReferenceKind, self).IsBackwardCompatible(rhs, checker)\n+            and self.is_nullable == rhs.is_nullable)\n+\n \n # Initialize the set of primitive types. These can be accessed by clients.\n BOOL = Kind('b')\n@@ -253,9 +287,13 @@ PRIMITIVES = (\n )\n \n ATTRIBUTE_MIN_VERSION = 'MinVersion'\n+ATTRIBUTE_DEFAULT = 'Default'\n ATTRIBUTE_EXTENSIBLE = 'Extensible'\n+ATTRIBUTE_NO_INTERRUPT = 'NoInterrupt'\n ATTRIBUTE_STABLE = 'Stable'\n ATTRIBUTE_SYNC = 'Sync'\n+ATTRIBUTE_UNLIMITED_SIZE = 'UnlimitedSize'\n+ATTRIBUTE_UUID = 'Uuid'\n \n \n class NamedValue(object):\n@@ -274,6 +312,9 @@ class NamedValue(object):\n             and (self.parent_kind, self.mojom_name) == (rhs.parent_kind,\n                                                         rhs.mojom_name))\n \n+  def __hash__(self):\n+    return hash((self.parent_kind, self.mojom_name))\n+\n \n class BuiltinValue(object):\n   def __init__(self, value):\n@@ -368,21 +409,19 @@ class Field(object):\n \n \n class StructField(Field):\n-  pass\n+  def __hash__(self):\n+    return super(Field, self).__hash__()\n \n \n class UnionField(Field):\n   pass\n \n \n-def _IsFieldBackwardCompatible(new_field, old_field):\n+def _IsFieldBackwardCompatible(new_field, old_field, checker):\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+  return checker.IsBackwardCompatible(new_field.kind, old_field.kind)\n \n \n class Struct(ReferenceKind):\n@@ -457,7 +496,7 @@ class Struct(ReferenceKind):\n     for constant in self.constants:\n       constant.Stylize(stylizer)\n \n-  def IsBackwardCompatible(self, older_struct):\n+  def IsBackwardCompatible(self, older_struct, checker):\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@@ -496,7 +535,7 @@ class Struct(ReferenceKind):\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+      if not _IsFieldBackwardCompatible(new_field, old_field, checker):\n         # Type or min-version mismatch between old and new versions of the same\n         # ordinal field.\n         return False\n@@ -590,7 +629,7 @@ class Union(ReferenceKind):\n     for field in self.fields:\n       field.Stylize(stylizer)\n \n-  def IsBackwardCompatible(self, older_union):\n+  def IsBackwardCompatible(self, older_union, checker):\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@@ -623,7 +662,7 @@ class Union(ReferenceKind):\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+      if not _IsFieldBackwardCompatible(new_field, old_field, checker):\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@@ -703,6 +742,10 @@ class Array(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return (isinstance(rhs, Array) and self.length == rhs.length\n+            and checker.IsBackwardCompatible(self.kind, rhs.kind))\n+\n \n class Map(ReferenceKind):\n   \"\"\"A map.\n@@ -747,6 +790,11 @@ class Map(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return (isinstance(rhs, Map)\n+            and checker.IsBackwardCompatible(self.key_kind, rhs.key_kind)\n+            and checker.IsBackwardCompatible(self.value_kind, rhs.value_kind))\n+\n \n class PendingRemote(ReferenceKind):\n   ReferenceKind.AddSharedProperty('kind')\n@@ -768,6 +816,10 @@ class PendingRemote(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return (isinstance(rhs, PendingRemote)\n+            and checker.IsBackwardCompatible(self.kind, rhs.kind))\n+\n \n class PendingReceiver(ReferenceKind):\n   ReferenceKind.AddSharedProperty('kind')\n@@ -789,6 +841,10 @@ class PendingReceiver(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return isinstance(rhs, PendingReceiver) and checker.IsBackwardCompatible(\n+        self.kind, rhs.kind)\n+\n \n class PendingAssociatedRemote(ReferenceKind):\n   ReferenceKind.AddSharedProperty('kind')\n@@ -810,6 +866,11 @@ class PendingAssociatedRemote(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return isinstance(rhs,\n+                      PendingAssociatedRemote) and checker.IsBackwardCompatible(\n+                          self.kind, rhs.kind)\n+\n \n class PendingAssociatedReceiver(ReferenceKind):\n   ReferenceKind.AddSharedProperty('kind')\n@@ -831,6 +892,11 @@ class PendingAssociatedReceiver(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return isinstance(\n+        rhs, PendingAssociatedReceiver) and checker.IsBackwardCompatible(\n+            self.kind, rhs.kind)\n+\n \n class InterfaceRequest(ReferenceKind):\n   ReferenceKind.AddSharedProperty('kind')\n@@ -851,6 +917,10 @@ class InterfaceRequest(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return isinstance(rhs, InterfaceRequest) and checker.IsBackwardCompatible(\n+        self.kind, rhs.kind)\n+\n \n class AssociatedInterfaceRequest(ReferenceKind):\n   ReferenceKind.AddSharedProperty('kind')\n@@ -873,6 +943,11 @@ class AssociatedInterfaceRequest(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return isinstance(\n+        rhs, AssociatedInterfaceRequest) and checker.IsBackwardCompatible(\n+            self.kind, rhs.kind)\n+\n \n class Parameter(object):\n   def __init__(self,\n@@ -976,6 +1051,16 @@ class Method(object):\n     return self.attributes.get(ATTRIBUTE_SYNC) \\\n         if self.attributes else None\n \n+  @property\n+  def allow_interrupt(self):\n+    return not self.attributes.get(ATTRIBUTE_NO_INTERRUPT) \\\n+        if self.attributes else True\n+\n+  @property\n+  def unlimited_message_size(self):\n+    return self.attributes.get(ATTRIBUTE_UNLIMITED_SIZE) \\\n+        if self.attributes else False\n+\n   def __eq__(self, rhs):\n     return (isinstance(rhs, Method) and\n             (self.mojom_name, self.ordinal, self.parameters,\n@@ -1029,7 +1114,7 @@ class Interface(ReferenceKind):\n     for constant in self.constants:\n       constant.Stylize(stylizer)\n \n-  def IsBackwardCompatible(self, older_interface):\n+  def IsBackwardCompatible(self, older_interface, checker):\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@@ -1067,8 +1152,8 @@ class Interface(ReferenceKind):\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+      if not checker.IsBackwardCompatible(new_method.param_struct,\n+                                          old_method.param_struct):\n         # The parameter list is not backward-compatible, which is not OK.\n         return False\n \n@@ -1081,8 +1166,8 @@ class Interface(ReferenceKind):\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+        if not checker.IsBackwardCompatible(new_method.response_param_struct,\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@@ -1120,6 +1205,20 @@ class Interface(ReferenceKind):\n                  self.attributes) == (rhs.mojom_name, rhs.methods, rhs.enums,\n                                       rhs.constants, rhs.attributes))\n \n+  @property\n+  def uuid(self):\n+    uuid_str = self.attributes.get(ATTRIBUTE_UUID) if self.attributes else None\n+    if uuid_str is None:\n+      return None\n+\n+    try:\n+      u = UUID(uuid_str)\n+    except:\n+      raise ValueError('Invalid format for Uuid attribute on interface {}. '\n+                       'Expected standard RFC 4122 string representation of '\n+                       'a UUID.'.format(self.mojom_name))\n+    return (int(u.hex[:16], 16), int(u.hex[16:], 16))\n+\n   def __hash__(self):\n     return id(self)\n \n@@ -1144,6 +1243,11 @@ class AssociatedInterface(ReferenceKind):\n   def __hash__(self):\n     return id(self)\n \n+  def IsBackwardCompatible(self, rhs, checker):\n+    return isinstance(rhs,\n+                      AssociatedInterface) and checker.IsBackwardCompatible(\n+                          self.kind, rhs.kind)\n+\n \n class EnumField(object):\n   def __init__(self,\n@@ -1160,6 +1264,11 @@ class EnumField(object):\n   def Stylize(self, stylizer):\n     self.name = stylizer.StylizeEnumField(self.mojom_name)\n \n+  @property\n+  def default(self):\n+    return self.attributes.get(ATTRIBUTE_DEFAULT, False) \\\n+        if self.attributes else False\n+\n   @property\n   def min_version(self):\n     return self.attributes.get(ATTRIBUTE_MIN_VERSION) \\\n@@ -1186,6 +1295,7 @@ class Enum(Kind):\n     self.attributes = attributes\n     self.min_value = None\n     self.max_value = None\n+    self.default_field = None\n \n   def Repr(self, as_ref=True):\n     if as_ref:\n@@ -1216,7 +1326,8 @@ class Enum(Kind):\n       prefix = self.module.GetNamespacePrefix()\n     return '%s%s' % (prefix, self.mojom_name)\n \n-  def IsBackwardCompatible(self, older_enum):\n+  # pylint: disable=unused-argument\n+  def IsBackwardCompatible(self, older_enum, checker):\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@@ -1250,9 +1361,10 @@ class Enum(Kind):\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+             self.min_value, self.max_value,\n+             self.default_field) == (rhs.mojom_name, rhs.native_only,\n+                                     rhs.fields, rhs.attributes, rhs.min_value,\n+                                     rhs.max_value, rhs.default_field))\n \n   def __hash__(self):\n     return id(self)\n@@ -1272,6 +1384,7 @@ class Module(object):\n     self.attributes = attributes\n     self.imports = []\n     self.imported_kinds = {}\n+    self.metadata = {}\n \n   def __repr__(self):\n     # Gives us a decent __repr__ for modules.\n@@ -1285,6 +1398,9 @@ class Module(object):\n                                   rhs.imports, rhs.constants, rhs.enums,\n                                   rhs.structs, rhs.unions, rhs.interfaces))\n \n+  def __hash__(self):\n+    return id(self)\n+\n   def Repr(self, as_ref=True):\n     if as_ref:\n       return '<%s path=%r mojom_namespace=%r>' % (\n@@ -1555,6 +1671,13 @@ def HasSyncMethods(interface):\n   return False\n \n \n+def HasUninterruptableMethods(interface):\n+  for method in interface.methods:\n+    if not method.allow_interrupt:\n+      return True\n+  return False\n+\n+\n def ContainsHandlesOrInterfaces(kind):\n   \"\"\"Check if the kind contains any handles.\n \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\nindex 7a300560..0da90058 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py\n@@ -75,9 +75,8 @@ def PrecompileTemplates(generator_modules, output_dir):\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)\n+    jinja_env.compile_templates(os.path.join(\n+        output_dir, \"%s.zip\" % generator.GetTemplatePrefix()),\n+                                extensions=[\"tmpl\"],\n+                                zip=\"stored\",\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\nindex d6df3ca6..7580b780 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py\n@@ -472,6 +472,9 @@ def _Method(module, parsed_method, interface):\n                     \"attribute. If no response parameters are needed, you \"\n                     \"could use an empty response parameter list, i.e., \"\n                     \"\\\"=> ()\\\".\")\n+  # And only methods with the [Sync] attribute can specify [NoInterrupt].\n+  if not method.allow_interrupt and not method.sync:\n+    raise Exception(\"Only [Sync] methods can be marked [NoInterrupt].\")\n \n   return method\n \n@@ -592,6 +595,16 @@ def _Enum(module, parsed_enum, parent_kind):\n         map(lambda field: _EnumField(module, enum, field),\n             parsed_enum.enum_value_list))\n     _ResolveNumericEnumValues(enum)\n+    # TODO(https://crbug.com/731893): Require a default value to be\n+    # specified.\n+    for field in enum.fields:\n+      if field.default:\n+        if not enum.extensible:\n+          raise Exception('Non-extensible enums may not specify a default')\n+        if enum.default_field is not None:\n+          raise Exception(\n+              'Only one enumerator value may be specified as the default')\n+        enum.default_field = field\n \n   module.kinds[enum.spec] = enum\n \n@@ -650,7 +663,9 @@ def _CollectReferencedKinds(module, all_defined_kinds):\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+    if (mojom.IsInterfaceRequestKind(kind) or mojom.IsAssociatedKind(kind)\n+        or mojom.IsPendingRemoteKind(kind)\n+        or mojom.IsPendingReceiverKind(kind)):\n       return [kind.kind]\n     if mojom.IsStructKind(kind):\n       return [kind]\n@@ -678,12 +693,9 @@ def _CollectReferencedKinds(module, all_defined_kinds):\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+        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 \ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom_parser.py b/utils/ipc/mojo/public/tools/mojom/mojom_parser.py\nindex 12adbfb9..eb90c825 100755\n--- a/utils/ipc/mojo/public/tools/mojom/mojom_parser.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom_parser.py\n@@ -14,6 +14,8 @@ import argparse\n import codecs\n import errno\n import json\n+import logging\n+import multiprocessing\n import os\n import os.path\n import sys\n@@ -25,6 +27,19 @@ from mojom.parse import parser\n from mojom.parse import conditional_features\n \n \n+# Disable this for easier debugging.\n+# In Python 2, subprocesses just hang when exceptions are thrown :(.\n+_ENABLE_MULTIPROCESSING = sys.version_info[0] > 2\n+\n+if sys.version_info < (3, 4):\n+  _MULTIPROCESSING_USES_FORK = sys.platform.startswith('linux')\n+else:\n+  # https://docs.python.org/3/library/multiprocessing.html#:~:text=bpo-33725\n+  if __name__ == '__main__' and sys.platform == 'darwin':\n+    multiprocessing.set_start_method('fork')\n+  _MULTIPROCESSING_USES_FORK = multiprocessing.get_start_method() == 'fork'\n+\n+\n def _ResolveRelativeImportPath(path, roots):\n   \"\"\"Attempts to resolve a relative import path against a set of possible roots.\n \n@@ -98,7 +113,7 @@ def _GetModuleFilename(mojom_filename):\n \n \n def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\n-                       dependencies, loaded_modules):\n+                       dependencies, loaded_modules, module_metadata):\n   \"\"\"Recursively ensures that a module and its dependencies are loaded.\n \n   Args:\n@@ -111,10 +126,8 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\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+    module_metadata: Metadata to be attached to every module loaded by this\n+        helper.\n \n   Returns:\n     None\n@@ -129,7 +142,7 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\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+                         loaded_modules, module_metadata)\n \n   imports = {}\n   for imp in asts[mojom_abspath].import_list:\n@@ -137,6 +150,7 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\n     imports[path] = loaded_modules[abs_paths[path]]\n   loaded_modules[mojom_abspath] = translate.OrderedModule(\n       asts[mojom_abspath], module_path, imports)\n+  loaded_modules[mojom_abspath].metadata = dict(module_metadata)\n \n \n def _CollectAllowedImportsFromBuildMetadata(build_metadata_filename):\n@@ -157,10 +171,67 @@ def _CollectAllowedImportsFromBuildMetadata(build_metadata_filename):\n   return allowed_imports\n \n \n+# multiprocessing helper.\n+def _ParseAstHelper(args):\n+  mojom_abspath, enabled_features = args\n+  with codecs.open(mojom_abspath, encoding='utf-8') as f:\n+    ast = parser.Parse(f.read(), mojom_abspath)\n+    conditional_features.RemoveDisabledDefinitions(ast, enabled_features)\n+    return mojom_abspath, ast\n+\n+\n+# multiprocessing helper.\n+def _SerializeHelper(args):\n+  mojom_abspath, mojom_path = args\n+  module_path = os.path.join(_SerializeHelper.output_root_path,\n+                             _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+    _SerializeHelper.loaded_modules[mojom_abspath].Dump(f)\n+\n+\n+def _Shard(target_func, args, processes=None):\n+  args = list(args)\n+  if processes is None:\n+    processes = multiprocessing.cpu_count()\n+  # Seems optimal to have each process perform at least 2 tasks.\n+  processes = min(processes, len(args) // 2)\n+\n+  if sys.platform == 'win32':\n+    # TODO(crbug.com/1190269) - we can't use more than 56\n+    # cores on Windows or Python3 may hang.\n+    processes = min(processes, 56)\n+\n+  # Don't spin up processes unless there is enough work to merit doing so.\n+  if not _ENABLE_MULTIPROCESSING or processes < 2:\n+    for result in map(target_func, args):\n+      yield result\n+    return\n+\n+  pool = multiprocessing.Pool(processes=processes)\n+  try:\n+    for result in pool.imap_unordered(target_func, args):\n+      yield result\n+  finally:\n+    pool.close()\n+    pool.join()  # Needed on Windows to avoid WindowsError during terminate.\n+    pool.terminate()\n+\n+\n def _ParseMojoms(mojom_files,\n                  input_root_paths,\n                  output_root_path,\n                  enabled_features,\n+                 module_metadata,\n                  allowed_imports=None):\n   \"\"\"Parses a set of mojom files and produces serialized module outputs.\n \n@@ -176,6 +247,8 @@ def _ParseMojoms(mojom_files,\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+    module_metadata: A list of 2-tuples representing metadata key-value pairs to\n+        attach to each compiled module output.\n \n   Returns:\n     None.\n@@ -193,72 +266,79 @@ def _ParseMojoms(mojom_files,\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+  logging.info('Parsing %d .mojom into ASTs', len(mojom_files_to_parse))\n+  map_args = ((mojom_abspath, enabled_features)\n+              for mojom_abspath in mojom_files_to_parse)\n+  for mojom_abspath, ast in _Shard(_ParseAstHelper, map_args):\n+    loaded_mojom_asts[mojom_abspath] = ast\n+\n+  logging.info('Processing dependencies')\n+  for mojom_abspath, ast in loaded_mojom_asts.items():\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(\n+            (import_abspath, imp.import_filename))\n+      elif import_abspath not in loaded_modules:\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'.join(invalid_imports)))\n+  logging.info('Loaded %d modules from dependencies', len(loaded_modules))\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+  logging.info('Ensuring inputs are loaded')\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+                       input_dependencies, loaded_modules, module_metadata)\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+  logging.info('Serializing %d modules', len(mojom_files_to_parse))\n+\n+  # Windows does not use fork() for multiprocessing, so we'd need to pass\n+  # loaded_module via IPC rather than via globals. Doing so is slower than not\n+  # using multiprocessing.\n+  _SerializeHelper.loaded_modules = loaded_modules\n+  _SerializeHelper.output_root_path = output_root_path\n+  # Doesn't seem to help past 4. Perhaps IO bound here?\n+  processes = 4 if _MULTIPROCESSING_USES_FORK else 0\n+  map_args = mojom_files_to_parse.items()\n+  for _ in _Shard(_SerializeHelper, map_args, processes=processes):\n+    pass\n \n \n def Run(command_line):\n+  debug_logging = os.environ.get('MOJOM_PARSER_DEBUG', '0') != '0'\n+  logging.basicConfig(level=logging.DEBUG if debug_logging else logging.WARNING,\n+                      format='%(levelname).1s %(relativeCreated)6d %(message)s')\n+  logging.info('Started (%s)', os.path.basename(sys.argv[0]))\n+\n   arg_parser = argparse.ArgumentParser(\n       description=\"\"\"\n Parses one or more mojom files and produces corresponding module outputs fully\n@@ -333,6 +413,16 @@ already present in the provided output root.\"\"\")\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+  arg_parser.add_argument(\n+      '--add-module-metadata',\n+      dest='module_metadata',\n+      default=[],\n+      action='append',\n+      metavar='KEY=VALUE',\n+      help='Adds a metadata key-value pair to the output module. This can be '\n+      'used by build toolchains to augment parsed mojom modules with product-'\n+      'specific metadata for later extraction and use by custom bindings '\n+      'generators.')\n \n   args, _ = arg_parser.parse_known_args(command_line)\n   if args.mojom_file_list:\n@@ -353,8 +443,14 @@ already present in the provided output root.\"\"\")\n   else:\n     allowed_imports = None\n \n+  module_metadata = list(\n+      map(lambda kvp: tuple(kvp.split('=')), args.module_metadata))\n   _ParseMojoms(mojom_files, input_roots, output_root, args.enabled_features,\n-               allowed_imports)\n+               module_metadata, allowed_imports)\n+  logging.info('Finished')\n+  # Exit without running GC, which can save multiple seconds due the large\n+  # number of object created.\n+  os._exit(0)\n \n \n if __name__ == '__main__':\ndiff --git a/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py b/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\nindex a0ee150e..65db4dc9 100644\n--- a/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\n+++ b/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\n@@ -2,6 +2,7 @@\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\n from mojom_parser_test_case import MojomParserTestCase\n \n \n@@ -20,9 +21,11 @@ class VersionCompatibilityTest(MojomParserTestCase):\n     self.assertEqual(set(old.keys()), set(new.keys()),\n                      'Old and new test mojoms should use the same type names.')\n \n+    checker = module.BackwardCompatibilityChecker()\n     compatibility_map = {}\n     for name in old.keys():\n-      compatibility_map[name] = new[name].IsBackwardCompatible(old[name])\n+      compatibility_map[name] = checker.IsBackwardCompatible(\n+          new[name], old[name])\n     return compatibility_map\n \n   def assertBackwardCompatible(self, old_mojom, new_mojom):\n@@ -234,6 +237,47 @@ class VersionCompatibilityTest(MojomParserTestCase):\n     self.assertNotBackwardCompatible('union U { string a; };',\n                                      'union U { string? a; };')\n \n+  def testFieldNestedTypeChanged(self):\n+    \"\"\"Changing the definition of a nested type within a field (such as an array\n+    element or interface endpoint type) should only break backward-compatibility\n+    if the changes to that type are not backward-compatible.\"\"\"\n+    self.assertBackwardCompatible(\n+        \"\"\"\\\n+        struct S { string a; };\n+        struct T { array<S> ss; };\n+        \"\"\", \"\"\"\\\n+        struct S {\n+          string a;\n+          [MinVersion=1] string? b;\n+        };\n+        struct T { array<S> ss; };\n+        \"\"\")\n+    self.assertBackwardCompatible(\n+        \"\"\"\\\n+        interface F { Do(); };\n+        struct S { pending_receiver<F> r; };\n+        \"\"\", \"\"\"\\\n+        interface F {\n+          Do();\n+          [MinVersion=1] Say();\n+        };\n+        struct S { pending_receiver<F> r; };\n+        \"\"\")\n+\n+  def testRecursiveTypeChange(self):\n+    \"\"\"Recursive types do not break the compatibility checker.\"\"\"\n+    self.assertBackwardCompatible(\n+        \"\"\"\\\n+        struct S {\n+          string a;\n+          array<S> others;\n+        };\"\"\", \"\"\"\\\n+        struct S {\n+          string a;\n+          array<S> others;\n+          [MinVersion=1] string? b;\n+        };\"\"\")\n+\n   def testUnionFieldBecomingNonOptional(self):\n     \"\"\"Changing a field from optional to non-optional breaks\n     backward-compatibility.\"\"\"\ndiff --git a/utils/ipc/tools/README b/utils/ipc/tools/README\nnew file mode 100644\nindex 00000000..d5c24fc3\n--- /dev/null\n+++ b/utils/ipc/tools/README\n@@ -0,0 +1,4 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+Files in this directory are imported from 9c138d992bfc of Chromium. Do not\n+modify them manually.\n",
    "prefixes": [
        "libcamera-devel",
        "v3",
        "2/2"
    ]
}