[libcamera-devel] [PATCH v1 1/1] utils: ipc: Update mojo

Harvey Yang chenghaoyang at chromium.org
Thu Mar 30 11:56:09 CEST 2023


Update mojo from the Chromium repository. The commit from which this was
taken is:
[white-space] Change DOM/HTML/SVG to set longhands of `white-space`

The update-mojo.sh script was used for this update.

Signed-off-by: Harvey Yang <chenghaoyang at chromium.org>
---
 utils/ipc/mojo/README                         |   2 +-
 utils/ipc/mojo/public/LICENSE                 |   2 +-
 utils/ipc/mojo/public/tools/BUILD.gn          |   8 +-
 utils/ipc/mojo/public/tools/bindings/BUILD.gn |  34 +-
 .../ipc/mojo/public/tools/bindings/README.md  | 148 ++-
 .../public/tools/bindings/checks/__init__.py  |   0
 .../bindings/checks/mojom_attributes_check.py | 168 ++++
 .../checks/mojom_attributes_check_unittest.py | 186 ++++
 .../checks/mojom_definitions_check.py         |  34 +
 .../checks/mojom_restrictions_check.py        | 102 +++
 .../mojom_restrictions_checks_unittest.py     | 254 ++++++
 .../tools/bindings/concatenate-files.py       |   5 +-
 ...concatenate_and_replace_closure_exports.py |   8 +-
 .../tools/bindings/gen_data_files_list.py     |   2 +-
 .../tools/bindings/generate_type_mappings.py  |   3 +-
 .../tools/bindings/minify_with_terser.py      |  47 +
 .../ipc/mojo/public/tools/bindings/mojom.gni  | 860 ++++++++++--------
 .../bindings/mojom_bindings_generator.py      |  62 +-
 .../mojom_bindings_generator_unittest.py      |   6 +-
 .../tools/bindings/validate_typemap_config.py |   4 +-
 utils/ipc/mojo/public/tools/mojom/BUILD.gn    |  17 +
 .../mojom/check_stable_mojom_compatibility.py |  46 +-
 ...eck_stable_mojom_compatibility_unittest.py |  87 +-
 .../mojo/public/tools/mojom/const_unittest.py |   2 +-
 .../mojo/public/tools/mojom/enum_unittest.py  |  30 +-
 .../mojo/public/tools/mojom/mojom/BUILD.gn    |   3 +-
 .../mojo/public/tools/mojom/mojom/error.py    |   2 +-
 .../mojo/public/tools/mojom/mojom/fileutil.py |   2 +-
 .../tools/mojom/mojom/fileutil_unittest.py    |   2 +-
 .../tools/mojom/mojom/generate/check.py       |  26 +
 .../tools/mojom/mojom/generate/generator.py   |   8 +-
 .../mojom/generate/generator_unittest.py      |   2 +-
 .../tools/mojom/mojom/generate/module.py      | 649 ++++++++-----
 .../mojom/mojom/generate/module_unittest.py   |   2 +-
 .../public/tools/mojom/mojom/generate/pack.py | 125 ++-
 .../mojom/mojom/generate/pack_unittest.py     |   2 +-
 .../mojom/mojom/generate/template_expander.py |   2 +-
 .../tools/mojom/mojom/generate/translate.py   | 408 ++++++++-
 .../mojom/generate/translate_unittest.py      |  39 +-
 .../public/tools/mojom/mojom/parse/ast.py     | 117 +--
 .../tools/mojom/mojom/parse/ast_unittest.py   |   6 +-
 .../mojom/mojom/parse/conditional_features.py |  14 +-
 .../parse/conditional_features_unittest.py    | 130 ++-
 .../public/tools/mojom/mojom/parse/lexer.py   |   6 +-
 .../tools/mojom/mojom/parse/lexer_unittest.py |   3 +-
 .../public/tools/mojom/mojom/parse/parser.py  |  24 +-
 .../mojom/mojom/parse/parser_unittest.py      |  34 +-
 .../mojo/public/tools/mojom/mojom_parser.py   | 119 ++-
 .../tools/mojom/mojom_parser_test_case.py     |   4 +-
 .../tools/mojom/mojom_parser_unittest.py      |  31 +-
 .../tools/mojom/stable_attribute_unittest.py  |   2 +-
 .../mojo/public/tools/mojom/union_unittest.py |  44 +
 .../mojom/version_compatibility_unittest.py   |  66 +-
 .../public/tools/run_all_python_unittests.py  |   8 +-
 utils/ipc/tools/README                        |   2 +-
 utils/ipc/tools/diagnosis/crbug_1001171.py    |   2 +-
 56 files changed, 3061 insertions(+), 940 deletions(-)
 create mode 100644 utils/ipc/mojo/public/tools/bindings/checks/__init__.py
 create mode 100644 utils/ipc/mojo/public/tools/bindings/checks/mojom_attributes_check.py
 create mode 100644 utils/ipc/mojo/public/tools/bindings/checks/mojom_attributes_check_unittest.py
 create mode 100644 utils/ipc/mojo/public/tools/bindings/checks/mojom_definitions_check.py
 create mode 100644 utils/ipc/mojo/public/tools/bindings/checks/mojom_restrictions_check.py
 create mode 100644 utils/ipc/mojo/public/tools/bindings/checks/mojom_restrictions_checks_unittest.py
 create mode 100755 utils/ipc/mojo/public/tools/bindings/minify_with_terser.py
 create mode 100644 utils/ipc/mojo/public/tools/mojom/BUILD.gn
 create mode 100644 utils/ipc/mojo/public/tools/mojom/mojom/generate/check.py
 create mode 100644 utils/ipc/mojo/public/tools/mojom/union_unittest.py

diff --git a/utils/ipc/mojo/README b/utils/ipc/mojo/README
index d5c24fc3..9a2979d3 100644
--- a/utils/ipc/mojo/README
+++ b/utils/ipc/mojo/README
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: CC0-1.0
 
-Files in this directory are imported from 9c138d992bfc of Chromium. Do not
+Files in this directory are imported from e2b2277a00e37 of Chromium. Do not
 modify them manually.
diff --git a/utils/ipc/mojo/public/LICENSE b/utils/ipc/mojo/public/LICENSE
index 972bb2ed..513e8a6a 100644
--- a/utils/ipc/mojo/public/LICENSE
+++ b/utils/ipc/mojo/public/LICENSE
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
diff --git a/utils/ipc/mojo/public/tools/BUILD.gn b/utils/ipc/mojo/public/tools/BUILD.gn
index eb6391a6..5328a34a 100644
--- a/utils/ipc/mojo/public/tools/BUILD.gn
+++ b/utils/ipc/mojo/public/tools/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -10,7 +10,11 @@ group("mojo_python_unittests") {
     "run_all_python_unittests.py",
     "//testing/scripts/run_isolated_script_test.py",
   ]
-  deps = [ "//mojo/public/tools/mojom/mojom:tests" ]
+  deps = [
+    "//mojo/public/tools/bindings:tests",
+    "//mojo/public/tools/mojom:tests",
+    "//mojo/public/tools/mojom/mojom:tests",
+  ]
   data_deps = [
     "//testing:test_scripts_shared",
     "//third_party/catapult/third_party/typ/",
diff --git a/utils/ipc/mojo/public/tools/bindings/BUILD.gn b/utils/ipc/mojo/public/tools/bindings/BUILD.gn
index 3e242532..203e476c 100644
--- a/utils/ipc/mojo/public/tools/bindings/BUILD.gn
+++ b/utils/ipc/mojo/public/tools/bindings/BUILD.gn
@@ -1,13 +1,11 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/python.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//third_party/jinja2/jinja2.gni")
 
-# TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-python2_action("precompile_templates") {
+action("precompile_templates") {
   sources = mojom_generator_sources
   sources += [
     "$mojom_generator_root/generators/cpp_templates/enum_macros.tmpl",
@@ -26,7 +24,6 @@ python2_action("precompile_templates") {
     "$mojom_generator_root/generators/cpp_templates/module-shared-message-ids.h.tmpl",
     "$mojom_generator_root/generators/cpp_templates/module-shared.cc.tmpl",
     "$mojom_generator_root/generators/cpp_templates/module-shared.h.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module-test-utils.cc.tmpl",
     "$mojom_generator_root/generators/cpp_templates/module-test-utils.h.tmpl",
     "$mojom_generator_root/generators/cpp_templates/module.cc.tmpl",
     "$mojom_generator_root/generators/cpp_templates/module.h.tmpl",
@@ -65,9 +62,6 @@ python2_action("precompile_templates") {
     "$mojom_generator_root/generators/java_templates/struct.java.tmpl",
     "$mojom_generator_root/generators/java_templates/union.java.tmpl",
     "$mojom_generator_root/generators/js_templates/enum_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/externs/interface_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/externs/module.externs.tmpl",
-    "$mojom_generator_root/generators/js_templates/externs/struct_definition.tmpl",
     "$mojom_generator_root/generators/js_templates/fuzzing.tmpl",
     "$mojom_generator_root/generators/js_templates/interface_definition.tmpl",
     "$mojom_generator_root/generators/js_templates/lite/enum_definition.tmpl",
@@ -93,8 +87,14 @@ python2_action("precompile_templates") {
     "$mojom_generator_root/generators/mojolpm_templates/mojolpm_macros.tmpl",
     "$mojom_generator_root/generators/mojolpm_templates/mojolpm_to_proto_macros.tmpl",
     "$mojom_generator_root/generators/mojolpm_templates/mojolpm_traits_specialization_macros.tmpl",
+    "$mojom_generator_root/generators/ts_templates/enum_definition.tmpl",
+    "$mojom_generator_root/generators/ts_templates/interface_definition.tmpl",
     "$mojom_generator_root/generators/ts_templates/module_definition.tmpl",
-    "$mojom_generator_root/generators/ts_templates/mojom.tmpl",
+    "$mojom_generator_root/generators/ts_templates/struct_definition.tmpl",
+    "$mojom_generator_root/generators/ts_templates/union_definition.tmpl",
+    "$mojom_generator_root/generators/webui_js_bridge_templates/webui_js_bridge_impl.cc.tmpl",
+    "$mojom_generator_root/generators/webui_js_bridge_templates/webui_js_bridge_impl.h.tmpl",
+    "$mojom_generator_root/generators/webui_js_bridge_templates/webui_js_bridge_macros.tmpl",
   ]
   script = mojom_generator_script
 
@@ -102,8 +102,9 @@ python2_action("precompile_templates") {
   outputs = [
     "$target_gen_dir/cpp_templates.zip",
     "$target_gen_dir/java_templates.zip",
-    "$target_gen_dir/mojolpm_templates.zip",
+    "$target_gen_dir/webui_js_bridge_templates.zip",
     "$target_gen_dir/js_templates.zip",
+    "$target_gen_dir/mojolpm_templates.zip",
     "$target_gen_dir/ts_templates.zip",
   ]
   args = [
@@ -113,3 +114,16 @@ python2_action("precompile_templates") {
     "precompile",
   ]
 }
+
+group("tests") {
+  data = [
+    mojom_generator_script,
+    "checks/mojom_attributes_check_unittest.py",
+    "checks/mojom_restrictions_checks_unittest.py",
+    "mojom_bindings_generator_unittest.py",
+    "//tools/diagnosis/crbug_1001171.py",
+    "//third_party/markupsafe/",
+  ]
+  data += mojom_generator_sources
+  data += jinja2_sources
+}
diff --git a/utils/ipc/mojo/public/tools/bindings/README.md b/utils/ipc/mojo/public/tools/bindings/README.md
index 43882450..683aa2f0 100644
--- a/utils/ipc/mojo/public/tools/bindings/README.md
+++ b/utils/ipc/mojo/public/tools/bindings/README.md
@@ -188,8 +188,8 @@ struct StringPair {
 };
 
 enum AnEnum {
-  YES,
-  NO
+  kYes,
+  kNo
 };
 
 interface SampleInterface {
@@ -209,7 +209,7 @@ struct AllTheThings {
   uint64 unsigned_64bit_value;
   float float_value_32bit;
   double float_value_64bit;
-  AnEnum enum_value = AnEnum.YES;
+  AnEnum enum_value = AnEnum.kYes;
 
   // Strings may be nullable.
   string? maybe_a_string_maybe_not;
@@ -300,14 +300,14 @@ within a module or nested within the namespace of some struct or interface:
 module business.mojom;
 
 enum Department {
-  SALES = 0,
-  DEV,
+  kSales = 0,
+  kDev,
 };
 
 struct Employee {
   enum Type {
-    FULL_TIME,
-    PART_TIME,
+    kFullTime,
+    kPartTime,
   };
 
   Type type;
@@ -315,6 +315,9 @@ struct Employee {
 };
 ```
 
+C++ constant-style enum value names are preferred as specified in the
+[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html#Enumerator_Names).
+
 Similar to C-style enums, individual values may be explicitly assigned within an
 enum definition. By default, values are based at zero and increment by
 1 sequentially.
@@ -336,8 +339,8 @@ struct Employee {
   const uint64 kInvalidId = 0;
 
   enum Type {
-    FULL_TIME,
-    PART_TIME,
+    kFullTime,
+    kPartTime,
   };
 
   uint64 id = kInvalidId;
@@ -396,20 +399,33 @@ interesting attributes supported today.
   extreme caution, because it can lead to deadlocks otherwise.
 
 * **`[Default]`**:
-  The `Default` attribute may be used to specify an enumerator value that
-  will be used if an `Extensible` enumeration does not deserialize to a known
-  value on the receiver side, i.e. the sender is using a newer version of the
-  enum. This allows unknown values to be mapped to a well-defined value that can
-  be appropriately handled.
+  The `Default` attribute may be used to specify an enumerator value or union
+  field that will be used if an `Extensible` enumeration or union does not
+  deserialize to a known value on the receiver side, i.e. the sender is using a
+  newer version of the enum or union. This allows unknown values to be mapped to
+  a well-defined value that can be appropriately handled.
+
+  Note: The `Default` field for a union must be of nullable or integral type.
+  When a union is defaulted to this field, the field takes on the default value
+  for its type: null for nullable types, and zero/false for integral types.
 
 * **`[Extensible]`**:
-  The `Extensible` attribute may be specified for any enum definition. This
-  essentially disables builtin range validation when receiving values of the
-  enum type in a message, allowing older bindings to tolerate unrecognized
-  values from newer versions of the enum.
+  The `Extensible` attribute may be specified for any enum or union definition.
+  For enums, this essentially disables builtin range validation when receiving
+  values of the enum type in a message, allowing older bindings to tolerate
+  unrecognized values from newer versions of the enum.
+
+  If an enum value within an extensible enum definition is affixed with the
+  `Default` attribute, out-of-range values for the enum will deserialize to that
+  default value. Only one enum value may be designated as the `Default`.
 
-  Note: in the future, an `Extensible` enumeration will require that a `Default`
-  enumerator value also be specified.
+  Similarly, a union marked `Extensible` will deserialize to its `Default` field
+  when an unrecognized field is received. Extensible unions MUST specify exactly
+  one `Default` field, and the field must be of nullable or integral type. When
+  defaulted to this field, the value is always null/zero/false as appropriate.
+
+  An `Extensible` enumeration REQUIRES that a `Default` value be specified,
+  so all new extensible enums should specify one.
 
 * **`[Native]`**:
   The `Native` attribute may be specified for an empty struct declaration to
@@ -422,7 +438,10 @@ interesting attributes supported today.
 * **`[MinVersion=N]`**:
   The `MinVersion` attribute is used to specify the version at which a given
   field, enum value, interface method, or method parameter was introduced.
-  See [Versioning](#Versioning) for more details.
+  See [Versioning](#Versioning) for more details. `MinVersion` does not apply
+  to interfaces, structs or enums, but to the fields of those types.
+  `MinVersion` is not a module-global value, but it is ok to pretend it is by
+  skipping versions when adding fields or parameters.
 
 * **`[Stable]`**:
   The `Stable` attribute specifies that a given mojom type or interface
@@ -448,7 +467,50 @@ interesting attributes supported today.
   matching `value` in the list of `enabled_features`, the definition will be
   disabled. This is useful for mojom definitions that only make sense on one
   platform. Note that the `EnableIf` attribute can only be set once per
-  definition.
+  definition and cannot be set at the same time as `EnableIfNot`. Also be aware
+  that only one condition can be tested, `EnableIf=value,xyz` introduces a new
+  `xyz` attribute. `xyz` is not part of the `EnableIf` condition that depends
+  only on the feature `value`. Complex conditions can be introduced via
+  enabled_features in `build.gn` files.
+
+* **`[EnableIfNot=value]`**:
+  The `EnableIfNot` attribute is used to conditionally enable definitions when
+  the mojom is parsed. If the `mojom` target in the GN file includes the
+  matching `value` in the list of `enabled_features`, the definition will be
+  disabled. This is useful for mojom definitions that only make sense on all but
+  one platform. Note that the `EnableIfNot` attribute can only be set once per
+  definition and cannot be set at the same time as `EnableIf`.
+
+* **`[ServiceSandbox=value]`**:
+  The `ServiceSandbox` attribute is used in Chromium to tag which sandbox a
+  service hosting an implementation of interface will be launched in. This only
+  applies to `C++` bindings. `value` should match a constant defined in an
+  imported `sandbox.mojom.Sandbox` enum (for Chromium this is
+  `//sandbox/policy/mojom/sandbox.mojom`), such as `kService`.
+
+* **`[RequireContext=enum]`**:
+  The `RequireContext` attribute is used in Chromium to tag interfaces that
+  should be passed (as remotes or receivers) only to privileged process
+  contexts. The process context must be an enum that is imported into the
+  mojom that defines the tagged interface. `RequireContext` may be used in
+  future to DCHECK or CHECK if remotes are made available in contexts that
+  conflict with the one provided in the interface definition. Process contexts
+  are not the same as the sandbox a process is running in, but will reflect
+  the set of capabilities provided to the service.
+
+* **`[AllowedContext=enum]`**:
+  The `AllowedContext` attribute is used in Chromium to tag methods that pass
+  remotes or receivers of interfaces that are marked with a `RequireContext`
+  attribute. The enum provided on the method must be equal or better (lower
+  numerically) than the one required on the interface being passed. At present
+  failing to specify an adequate `AllowedContext` value will cause mojom
+  generation to fail at compile time. In future DCHECKs or CHECKs might be
+  added to enforce that method is only called from a process context that meets
+  the given `AllowedContext` value. The enum must of the same type as that
+  specified in the interface's `RequireContext` attribute. Adding an
+  `AllowedContext` attribute to a method is a strong indication that you need
+   a detailed security review of your design - please reach out to the security
+   team.
 
 ## Generated Code For Target Languages
 
@@ -495,9 +557,9 @@ values. For example if a Mojom declares the enum:
 
 ``` cpp
 enum AdvancedBoolean {
-  TRUE = 0,
-  FALSE = 1,
-  FILE_NOT_FOUND = 2,
+  kTrue = 0,
+  kFalse = 1,
+  kFileNotFound = 2,
 };
 ```
 
@@ -550,10 +612,16 @@ See the documentation for
 
 *** note
 **NOTE:** You don't need to worry about versioning if you don't care about
-backwards compatibility. Specifically, all parts of Chrome are updated
-atomically today and there is not yet any possibility of any two Chrome
-processes communicating with two different versions of any given Mojom
-interface.
+backwards compatibility. Today, all parts of the Chrome browser are
+updated atomically and there is not yet any possibility of any two
+Chrome processes communicating with two different versions of any given Mojom
+interface. On Chrome OS, there are several places where versioning is required.
+For example,
+[ARC++](https://developer.android.com/chrome-os/intro)
+uses versioned mojo to send IPC to the Android container.
+Likewise, the
+[Lacros](/docs/lacros.md)
+browser uses versioned mojo to talk to the ash system UI.
 ***
 
 Services extend their interfaces to support new features over time, and clients
@@ -593,8 +661,8 @@ struct Employee {
 
 *** note
 **NOTE:** Mojo object or handle types added with a `MinVersion` **MUST** be
-optional (nullable). See [Primitive Types](#Primitive-Types) for details on
-nullable values.
+optional (nullable) or primitive. See [Primitive Types](#Primitive-Types) for
+details on nullable values.
 ***
 
 By default, fields belong to version 0. New fields must be appended to the
@@ -624,10 +692,10 @@ the following hard constraints:
 * For any given struct or interface, if any field or method explicitly specifies
     an ordinal value, all fields or methods must explicitly specify an ordinal
     value.
-* For an *N*-field struct or *N*-method interface, the set of explicitly
-    assigned ordinal values must be limited to the range *[0, N-1]*. Interfaces
-    should include placeholder methods to fill the ordinal positions of removed
-    methods (for example "Unused_Message_7 at 7()" or "RemovedMessage at 42()", etc).
+* For an *N*-field struct, the set of explicitly assigned ordinal values must be
+    limited to the range *[0, N-1]*. Structs should include placeholder fields
+    to fill the ordinal positions of removed fields (for example "Unused_Field"
+    or "RemovedField", etc).
 
 You may reorder fields, but you must ensure that the ordinal values of existing
 fields remain unchanged. For example, the following struct remains
@@ -712,8 +780,8 @@ If you want an enum to be extensible in the future, you can apply the
 ``` cpp
 [Extensible]
 enum Department {
-  SALES,
-  DEV,
+  kSales,
+  kDev,
 };
 ```
 
@@ -722,9 +790,9 @@ And later you can extend this enum without breaking backwards compatibility:
 ``` cpp
 [Extensible]
 enum Department {
-  SALES,
-  DEV,
-  [MinVersion=1] RESEARCH,
+  kSales,
+  kDev,
+  [MinVersion=1] kResearch,
 };
 ```
 
diff --git a/utils/ipc/mojo/public/tools/bindings/checks/__init__.py b/utils/ipc/mojo/public/tools/bindings/checks/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/utils/ipc/mojo/public/tools/bindings/checks/mojom_attributes_check.py b/utils/ipc/mojo/public/tools/bindings/checks/mojom_attributes_check.py
new file mode 100644
index 00000000..3a2d2a3b
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/bindings/checks/mojom_attributes_check.py
@@ -0,0 +1,168 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Validate mojo attributes are allowed in Chrome before generation."""
+
+import mojom.generate.check as check
+import mojom.generate.module as module
+
+_COMMON_ATTRIBUTES = {
+    'EnableIf',
+    'EnableIfNot',
+}
+
+# For struct, union & parameter lists.
+_COMMON_FIELD_ATTRIBUTES = _COMMON_ATTRIBUTES | {
+    'MinVersion',
+    'RenamedFrom',
+}
+
+# Note: `Default`` goes on the default _value_, not on the enum.
+# Note: [Stable] without [Extensible] is not allowed.
+_ENUM_ATTRIBUTES = _COMMON_ATTRIBUTES | {
+    'Extensible',
+    'Native',
+    'Stable',
+    'RenamedFrom',
+    'Uuid',
+}
+
+# TODO(crbug.com/1234883) MinVersion is not needed for EnumVal.
+_ENUMVAL_ATTRIBUTES = _COMMON_ATTRIBUTES | {
+    'Default',
+    'MinVersion',
+}
+
+_INTERFACE_ATTRIBUTES = _COMMON_ATTRIBUTES | {
+    'WebUIJsBridge',
+    'RenamedFrom',
+    'RequireContext',
+    'ServiceSandbox',
+    'Stable',
+    'Uuid',
+}
+
+_METHOD_ATTRIBUTES = _COMMON_ATTRIBUTES | {
+    'AllowedContext',
+    'MinVersion',
+    'NoInterrupt',
+    'Sync',
+    'UnlimitedSize',
+}
+
+_MODULE_ATTRIBUTES = _COMMON_ATTRIBUTES | {
+    'JavaConstantsClassName',
+    'JavaPackage',
+}
+
+_PARAMETER_ATTRIBUTES = _COMMON_FIELD_ATTRIBUTES
+
+_STRUCT_ATTRIBUTES = _COMMON_ATTRIBUTES | {
+    'CustomSerializer',
+    'JavaClassName',
+    'Native',
+    'Stable',
+    'RenamedFrom',
+    'Uuid',
+}
+
+_STRUCT_FIELD_ATTRIBUTES = _COMMON_FIELD_ATTRIBUTES
+
+_UNION_ATTRIBUTES = _COMMON_ATTRIBUTES | {
+    'Extensible',
+    'Stable',
+    'RenamedFrom',
+    'Uuid',
+}
+
+_UNION_FIELD_ATTRIBUTES = _COMMON_FIELD_ATTRIBUTES | {
+    'Default',
+}
+
+# TODO(https://crbug.com/1193875) empty this set and remove the allowlist.
+_STABLE_ONLY_ALLOWLISTED_ENUMS = {
+    'crosapi.mojom.OptionalBool',
+    'crosapi.mojom.TriState',
+}
+
+
+class Check(check.Check):
+  def __init__(self, *args, **kwargs):
+    super(Check, self).__init__(*args, **kwargs)
+
+  def _Respell(self, allowed, attribute):
+    for a in allowed:
+      if a.lower() == attribute.lower():
+        return f" - Did you mean: {a}?"
+    return ""
+
+  def _CheckAttributes(self, context, allowed, attributes):
+    if not attributes:
+      return
+    for attribute in attributes:
+      if not attribute in allowed:
+        # Is there a close misspelling?
+        hint = self._Respell(allowed, attribute)
+        raise check.CheckException(
+            self.module,
+            f"attribute {attribute} not allowed on {context}{hint}")
+
+  def _CheckEnumAttributes(self, enum):
+    if enum.attributes:
+      self._CheckAttributes("enum", _ENUM_ATTRIBUTES, enum.attributes)
+      if 'Stable' in enum.attributes and not 'Extensible' in enum.attributes:
+        full_name = f"{self.module.mojom_namespace}.{enum.mojom_name}"
+        if full_name not in _STABLE_ONLY_ALLOWLISTED_ENUMS:
+          raise check.CheckException(
+              self.module,
+              f"[Extensible] required on [Stable] enum {full_name}")
+    for enumval in enum.fields:
+      self._CheckAttributes("enum value", _ENUMVAL_ATTRIBUTES,
+                            enumval.attributes)
+
+  def _CheckInterfaceAttributes(self, interface):
+    self._CheckAttributes("interface", _INTERFACE_ATTRIBUTES,
+                          interface.attributes)
+    for method in interface.methods:
+      self._CheckAttributes("method", _METHOD_ATTRIBUTES, method.attributes)
+      for param in method.parameters:
+        self._CheckAttributes("parameter", _PARAMETER_ATTRIBUTES,
+                              param.attributes)
+      if method.response_parameters:
+        for param in method.response_parameters:
+          self._CheckAttributes("parameter", _PARAMETER_ATTRIBUTES,
+                                param.attributes)
+    for enum in interface.enums:
+      self._CheckEnumAttributes(enum)
+
+  def _CheckModuleAttributes(self):
+    self._CheckAttributes("module", _MODULE_ATTRIBUTES, self.module.attributes)
+
+  def _CheckStructAttributes(self, struct):
+    self._CheckAttributes("struct", _STRUCT_ATTRIBUTES, struct.attributes)
+    for field in struct.fields:
+      self._CheckAttributes("struct field", _STRUCT_FIELD_ATTRIBUTES,
+                            field.attributes)
+    for enum in struct.enums:
+      self._CheckEnumAttributes(enum)
+
+  def _CheckUnionAttributes(self, union):
+    self._CheckAttributes("union", _UNION_ATTRIBUTES, union.attributes)
+    for field in union.fields:
+      self._CheckAttributes("union field", _UNION_FIELD_ATTRIBUTES,
+                            field.attributes)
+
+  def CheckModule(self):
+    """Note that duplicate attributes are forbidden at the parse phase.
+    We also do not need to look at the types of any parameters, as they will be
+    checked where they are defined. Consts do not have attributes so can be
+    skipped."""
+    self._CheckModuleAttributes()
+    for interface in self.module.interfaces:
+      self._CheckInterfaceAttributes(interface)
+    for enum in self.module.enums:
+      self._CheckEnumAttributes(enum)
+    for struct in self.module.structs:
+      self._CheckStructAttributes(struct)
+    for union in self.module.unions:
+      self._CheckUnionAttributes(union)
diff --git a/utils/ipc/mojo/public/tools/bindings/checks/mojom_attributes_check_unittest.py b/utils/ipc/mojo/public/tools/bindings/checks/mojom_attributes_check_unittest.py
new file mode 100644
index 00000000..8c7f3c2c
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/bindings/checks/mojom_attributes_check_unittest.py
@@ -0,0 +1,186 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import mojom.generate.check as check
+from mojom_bindings_generator import LoadChecks, _Generate
+from mojom_parser_test_case import MojomParserTestCase
+
+
+class FakeArgs:
+  """Fakes args to _Generate - intention is to do just enough to run checks"""
+
+  def __init__(self, tester, files=None):
+    """ `tester` is MojomParserTestCase for paths.
+        `files` will have tester path added."""
+    self.checks_string = 'attributes'
+    self.depth = tester.GetPath('')
+    self.filelist = None
+    self.filename = [tester.GetPath(x) for x in files]
+    self.gen_directories = tester.GetPath('gen')
+    self.generators_string = ''
+    self.import_directories = []
+    self.output_dir = tester.GetPath('out')
+    self.scrambled_message_id_salt_paths = None
+    self.typemaps = []
+    self.variant = 'none'
+
+
+class MojoBindingsCheckTest(MojomParserTestCase):
+  def _ParseAndGenerate(self, mojoms):
+    self.ParseMojoms(mojoms)
+    args = FakeArgs(self, files=mojoms)
+    _Generate(args, {})
+
+  def _testValid(self, filename, content):
+    self.WriteFile(filename, content)
+    self._ParseAndGenerate([filename])
+
+  def _testThrows(self, filename, content, regexp):
+    mojoms = []
+    self.WriteFile(filename, content)
+    mojoms.append(filename)
+    with self.assertRaisesRegexp(check.CheckException, regexp):
+      self._ParseAndGenerate(mojoms)
+
+  def testLoads(self):
+    """Validate that the check is registered under the expected name."""
+    check_modules = LoadChecks('attributes')
+    self.assertTrue(check_modules['attributes'])
+
+  def testNoAnnotations(self):
+    # Undecorated mojom should be fine.
+    self._testValid(
+        "a.mojom", """
+      module a;
+      struct Bar { int32 a; };
+      enum Hello { kValue };
+      union Thingy { Bar b; Hello hi; };
+      interface Foo {
+        Foo(int32 a, Hello hi, Thingy t) => (Bar b);
+      };
+    """)
+
+  def testValidAnnotations(self):
+    # Obviously this is meaningless and won't generate, but it should pass
+    # the attribute check's validation.
+    self._testValid(
+        "a.mojom", """
+      [JavaConstantsClassName="FakeClass",JavaPackage="org.chromium.Fake"]
+      module a;
+      [Stable, Extensible]
+      enum Hello { [Default] kValue, kValue2, [MinVersion=2] kValue3 };
+      [Native]
+      enum NativeEnum {};
+      [Stable,Extensible]
+      union Thingy { Bar b; [Default]int32 c; Hello hi; };
+
+      [Stable,RenamedFrom="module.other.Foo",
+       Uuid="4C178401-4B07-4C2E-9255-5401A943D0C7"]
+      struct Structure { Hello hi; };
+
+      [ServiceSandbox=Hello.kValue,RequireContext=Hello.kValue,Stable,
+       Uuid="2F17D7DD-865A-4B1C-9394-9C94E035E82F"]
+      interface Foo {
+        [AllowedContext=Hello.kValue]
+        Foo at 0(int32 a) => (int32 b);
+        [MinVersion=2,Sync,UnlimitedSize,NoInterrupt]
+        Bar at 1(int32 b, [MinVersion=2]Structure? s) => (bool c);
+      };
+    """)
+
+  def testWrongModuleStable(self):
+    contents = """
+      // err: module cannot be Stable
+      [Stable]
+      module a;
+      enum Hello { kValue, kValue2, kValue3 };
+      enum NativeEnum {};
+      struct Structure { Hello hi; };
+
+      interface Foo {
+        Foo(int32 a) => (int32 b);
+        Bar(int32 b, Structure? s) => (bool c);
+      };
+    """
+    self._testThrows('b.mojom', contents,
+                     'attribute Stable not allowed on module')
+
+  def testWrongEnumDefault(self):
+    contents = """
+      module a;
+      // err: default should go on EnumValue not Enum.
+      [Default=kValue]
+      enum Hello { kValue, kValue2, kValue3 };
+      enum NativeEnum {};
+      struct Structure { Hello hi; };
+
+      interface Foo {
+        Foo(int32 a) => (int32 b);
+        Bar(int32 b, Structure? s) => (bool c);
+      };
+    """
+    self._testThrows('b.mojom', contents,
+                     'attribute Default not allowed on enum')
+
+  def testWrongStructMinVersion(self):
+    contents = """
+      module a;
+      enum Hello { kValue, kValue2, kValue3 };
+      enum NativeEnum {};
+      // err: struct cannot have MinVersion.
+      [MinVersion=2]
+      struct Structure { Hello hi; };
+
+      interface Foo {
+        Foo(int32 a) => (int32 b);
+        Bar(int32 b, Structure? s) => (bool c);
+      };
+    """
+    self._testThrows('b.mojom', contents,
+                     'attribute MinVersion not allowed on struct')
+
+  def testWrongMethodRequireContext(self):
+    contents = """
+      module a;
+      enum Hello { kValue, kValue2, kValue3 };
+      enum NativeEnum {};
+      struct Structure { Hello hi; };
+
+      interface Foo {
+        // err: RequireContext is for interfaces.
+        [RequireContext=Hello.kValue]
+        Foo(int32 a) => (int32 b);
+        Bar(int32 b, Structure? s) => (bool c);
+      };
+    """
+    self._testThrows('b.mojom', contents,
+                     'RequireContext not allowed on method')
+
+  def testWrongMethodRequireContext(self):
+    # crbug.com/1230122
+    contents = """
+      module a;
+      interface Foo {
+        // err: sync not Sync.
+        [sync]
+        Foo(int32 a) => (int32 b);
+      };
+    """
+    self._testThrows('b.mojom', contents,
+                     'attribute sync not allowed.*Did you mean: Sync')
+
+  def testStableExtensibleEnum(self):
+    # crbug.com/1193875
+    contents = """
+      module a;
+      [Stable]
+      enum Foo {
+        kDefaultVal,
+        kOtherVal = 2,
+      };
+    """
+    self._testThrows('a.mojom', contents,
+                     'Extensible.*?required.*?Stable.*?enum')
diff --git a/utils/ipc/mojo/public/tools/bindings/checks/mojom_definitions_check.py b/utils/ipc/mojo/public/tools/bindings/checks/mojom_definitions_check.py
new file mode 100644
index 00000000..702d41c3
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/bindings/checks/mojom_definitions_check.py
@@ -0,0 +1,34 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Ensure no duplicate type definitions before generation."""
+
+import mojom.generate.check as check
+import mojom.generate.module as module
+
+
+class Check(check.Check):
+  def __init__(self, *args, **kwargs):
+    super(Check, self).__init__(*args, **kwargs)
+
+  def CheckModule(self):
+    kinds = dict()
+    for module in self.module.imports:
+      for kind in module.enums + module.structs + module.unions:
+        kind_name = f'{kind.module.mojom_namespace}.{kind.mojom_name}'
+        if kind_name in kinds:
+          previous_module = kinds[kind_name]
+          if previous_module.path != module.path:
+            raise check.CheckException(
+                self.module, f"multiple-definition for type {kind_name}" +
+                f"(defined in both {previous_module} and {module})")
+        kinds[kind_name] = kind.module
+
+    for kind in self.module.enums + self.module.structs + self.module.unions:
+      kind_name = f'{kind.module.mojom_namespace}.{kind.mojom_name}'
+      if kind_name in kinds:
+        previous_module = kinds[kind_name]
+        raise check.CheckException(
+            self.module, f"multiple-definition for type {kind_name}" +
+            f"(previous definition in {previous_module})")
+    return True
diff --git a/utils/ipc/mojo/public/tools/bindings/checks/mojom_restrictions_check.py b/utils/ipc/mojo/public/tools/bindings/checks/mojom_restrictions_check.py
new file mode 100644
index 00000000..d570e26c
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/bindings/checks/mojom_restrictions_check.py
@@ -0,0 +1,102 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Validate RequireContext and AllowedContext annotations before generation."""
+
+import mojom.generate.check as check
+import mojom.generate.module as module
+
+
+class Check(check.Check):
+  def __init__(self, *args, **kwargs):
+    self.kind_to_interfaces = dict()
+    super(Check, self).__init__(*args, **kwargs)
+
+  def _IsPassedInterface(self, candidate):
+    if isinstance(
+        candidate.kind,
+        (module.PendingReceiver, module.PendingRemote,
+         module.PendingAssociatedReceiver, module.PendingAssociatedRemote)):
+      return True
+    return False
+
+  def _CheckInterface(self, method, param):
+    # |param| is a pending_x<Interface> so need .kind.kind to get Interface.
+    interface = param.kind.kind
+    if interface.require_context:
+      if method.allowed_context is None:
+        raise check.CheckException(
+            self.module, "method `{}` has parameter `{}` which passes interface"
+            " `{}` that requires an AllowedContext annotation but none exists.".
+            format(
+                method.mojom_name,
+                param.mojom_name,
+                interface.mojom_name,
+            ))
+      # If a string was provided, or if an enum was not imported, this will
+      # be a string and we cannot validate that it is in range.
+      if not isinstance(method.allowed_context, module.EnumValue):
+        raise check.CheckException(
+            self.module,
+            "method `{}` has AllowedContext={} which is not a valid enum value."
+            .format(method.mojom_name, method.allowed_context))
+      # EnumValue must be from the same enum to be compared.
+      if interface.require_context.enum != method.allowed_context.enum:
+        raise check.CheckException(
+            self.module, "method `{}` has parameter `{}` which passes interface"
+            " `{}` that requires AllowedContext={} but one of kind `{}` was "
+            "provided.".format(
+                method.mojom_name,
+                param.mojom_name,
+                interface.mojom_name,
+                interface.require_context.enum,
+                method.allowed_context.enum,
+            ))
+      # RestrictContext enums have most privileged field first (lowest value).
+      interface_value = interface.require_context.field.numeric_value
+      method_value = method.allowed_context.field.numeric_value
+      if interface_value < method_value:
+        raise check.CheckException(
+            self.module, "RequireContext={} > AllowedContext={} for method "
+            "`{}` which passes interface `{}`.".format(
+                interface.require_context.GetSpec(),
+                method.allowed_context.GetSpec(), method.mojom_name,
+                interface.mojom_name))
+      return True
+
+  def _GatherReferencedInterfaces(self, field):
+    key = field.kind.spec
+    # structs/unions can nest themselves so we need to bookkeep.
+    if not key in self.kind_to_interfaces:
+      # Might reference ourselves so have to create the list first.
+      self.kind_to_interfaces[key] = set()
+      for param in field.kind.fields:
+        if self._IsPassedInterface(param):
+          self.kind_to_interfaces[key].add(param)
+        elif isinstance(param.kind, (module.Struct, module.Union)):
+          for iface in self._GatherReferencedInterfaces(param):
+            self.kind_to_interfaces[key].add(iface)
+    return self.kind_to_interfaces[key]
+
+  def _CheckParams(self, method, params):
+    # Note: we have to repeat _CheckParams for each method as each might have
+    # different AllowedContext= attributes. We cannot memoize this function,
+    # but can do so for gathering referenced interfaces as their RequireContext
+    # attributes do not change.
+    for param in params:
+      if self._IsPassedInterface(param):
+        self._CheckInterface(method, param)
+      elif isinstance(param.kind, (module.Struct, module.Union)):
+        for interface in self._GatherReferencedInterfaces(param):
+          self._CheckInterface(method, interface)
+
+  def _CheckMethod(self, method):
+    if method.parameters:
+      self._CheckParams(method, method.parameters)
+    if method.response_parameters:
+      self._CheckParams(method, method.response_parameters)
+
+  def CheckModule(self):
+    for interface in self.module.interfaces:
+      for method in interface.methods:
+        self._CheckMethod(method)
diff --git a/utils/ipc/mojo/public/tools/bindings/checks/mojom_restrictions_checks_unittest.py b/utils/ipc/mojo/public/tools/bindings/checks/mojom_restrictions_checks_unittest.py
new file mode 100644
index 00000000..a6cd71e2
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/bindings/checks/mojom_restrictions_checks_unittest.py
@@ -0,0 +1,254 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import mojom.generate.check as check
+from mojom_bindings_generator import LoadChecks, _Generate
+from mojom_parser_test_case import MojomParserTestCase
+
+# Mojoms that we will use in multiple tests.
+basic_mojoms = {
+    'level.mojom':
+    """
+  module level;
+  enum Level {
+    kHighest,
+    kMiddle,
+    kLowest,
+  };
+  """,
+    'interfaces.mojom':
+    """
+  module interfaces;
+  import "level.mojom";
+  struct Foo {int32 bar;};
+  [RequireContext=level.Level.kHighest]
+  interface High {
+    DoFoo(Foo foo);
+  };
+  [RequireContext=level.Level.kMiddle]
+  interface Mid {
+    DoFoo(Foo foo);
+  };
+  [RequireContext=level.Level.kLowest]
+  interface Low {
+    DoFoo(Foo foo);
+  };
+  """
+}
+
+
+class FakeArgs:
+  """Fakes args to _Generate - intention is to do just enough to run checks"""
+
+  def __init__(self, tester, files=None):
+    """ `tester` is MojomParserTestCase for paths.
+        `files` will have tester path added."""
+    self.checks_string = 'restrictions'
+    self.depth = tester.GetPath('')
+    self.filelist = None
+    self.filename = [tester.GetPath(x) for x in files]
+    self.gen_directories = tester.GetPath('gen')
+    self.generators_string = ''
+    self.import_directories = []
+    self.output_dir = tester.GetPath('out')
+    self.scrambled_message_id_salt_paths = None
+    self.typemaps = []
+    self.variant = 'none'
+
+
+class MojoBindingsCheckTest(MojomParserTestCase):
+  def _WriteBasicMojoms(self):
+    for filename, contents in basic_mojoms.items():
+      self.WriteFile(filename, contents)
+    return list(basic_mojoms.keys())
+
+  def _ParseAndGenerate(self, mojoms):
+    self.ParseMojoms(mojoms)
+    args = FakeArgs(self, files=mojoms)
+    _Generate(args, {})
+
+  def testLoads(self):
+    """Validate that the check is registered under the expected name."""
+    check_modules = LoadChecks('restrictions')
+    self.assertTrue(check_modules['restrictions'])
+
+  def testValidAnnotations(self):
+    mojoms = self._WriteBasicMojoms()
+
+    a = 'a.mojom'
+    self.WriteFile(
+        a, """
+      module a;
+      import "level.mojom";
+      import "interfaces.mojom";
+
+      interface PassesHigh {
+        [AllowedContext=level.Level.kHighest]
+        DoHigh(pending_receiver<interfaces.High> hi);
+      };
+      interface PassesMedium {
+        [AllowedContext=level.Level.kMiddle]
+        DoMedium(pending_receiver<interfaces.Mid> hi);
+        [AllowedContext=level.Level.kMiddle]
+        DoMediumRem(pending_remote<interfaces.Mid> hi);
+        [AllowedContext=level.Level.kMiddle]
+        DoMediumAssoc(pending_associated_receiver<interfaces.Mid> hi);
+        [AllowedContext=level.Level.kMiddle]
+        DoMediumAssocRem(pending_associated_remote<interfaces.Mid> hi);
+      };
+      interface PassesLow {
+        [AllowedContext=level.Level.kLowest]
+        DoLow(pending_receiver<interfaces.Low> hi);
+      };
+
+      struct One { pending_receiver<interfaces.High> hi; };
+      struct Two { One one; };
+      interface PassesNestedHigh {
+        [AllowedContext=level.Level.kHighest]
+        DoNestedHigh(Two two);
+      };
+
+      // Allowed as PassesHigh is not itself restricted.
+      interface PassesPassesHigh {
+        DoPass(pending_receiver<PassesHigh> hiho);
+      };
+    """)
+    mojoms.append(a)
+    self._ParseAndGenerate(mojoms)
+
+  def _testThrows(self, filename, content, regexp):
+    mojoms = self._WriteBasicMojoms()
+    self.WriteFile(filename, content)
+    mojoms.append(filename)
+    with self.assertRaisesRegexp(check.CheckException, regexp):
+      self._ParseAndGenerate(mojoms)
+
+  def testMissingAnnotation(self):
+    contents = """
+      module b;
+      import "level.mojom";
+      import "interfaces.mojom";
+
+      interface PassesHigh {
+        // err: missing annotation.
+        DoHigh(pending_receiver<interfaces.High> hi);
+      };
+    """
+    self._testThrows('b.mojom', contents, 'require.*?AllowedContext')
+
+  def testAllowTooLow(self):
+    contents = """
+      module b;
+      import "level.mojom";
+      import "interfaces.mojom";
+
+      interface PassesHigh {
+        // err: level is worse than required.
+        [AllowedContext=level.Level.kMiddle]
+        DoHigh(pending_receiver<interfaces.High> hi);
+      };
+    """
+    self._testThrows('b.mojom', contents,
+                     'RequireContext=.*?kHighest > AllowedContext=.*?kMiddle')
+
+  def testWrongEnumInAllow(self):
+    contents = """
+      module b;
+      import "level.mojom";
+      import "interfaces.mojom";
+      enum Blah {
+        kZero,
+      };
+      interface PassesHigh {
+        // err: different enums.
+        [AllowedContext=Blah.kZero]
+        DoHigh(pending_receiver<interfaces.High> hi);
+      };
+    """
+    self._testThrows('b.mojom', contents, 'but one of kind')
+
+  def testNotAnEnumInAllow(self):
+    contents = """
+      module b;
+      import "level.mojom";
+      import "interfaces.mojom";
+      interface PassesHigh {
+        // err: not an enum.
+        [AllowedContext=doopdedoo.mojom.kWhatever]
+        DoHigh(pending_receiver<interfaces.High> hi);
+      };
+    """
+    self._testThrows('b.mojom', contents, 'not a valid enum value')
+
+  def testMissingAllowedForNestedStructs(self):
+    contents = """
+      module b;
+      import "level.mojom";
+      import "interfaces.mojom";
+      struct One { pending_receiver<interfaces.High> hi; };
+      struct Two { One one; };
+      interface PassesNestedHigh {
+        // err: missing annotation.
+        DoNestedHigh(Two two);
+      };
+    """
+    self._testThrows('b.mojom', contents, 'require.*?AllowedContext')
+
+  def testMissingAllowedForNestedUnions(self):
+    contents = """
+      module b;
+      import "level.mojom";
+      import "interfaces.mojom";
+      struct One { pending_receiver<interfaces.High> hi; };
+      struct Two { One one; };
+      union Three {One one; Two two; };
+      interface PassesNestedHigh {
+        // err: missing annotation.
+        DoNestedHigh(Three three);
+      };
+    """
+    self._testThrows('b.mojom', contents, 'require.*?AllowedContext')
+
+  def testMultipleInterfacesThrows(self):
+    contents = """
+      module b;
+      import "level.mojom";
+      import "interfaces.mojom";
+      struct One { pending_receiver<interfaces.High> hi; };
+      interface PassesMultipleInterfaces {
+        [AllowedContext=level.Level.kMiddle]
+        DoMultiple(
+          pending_remote<interfaces.Mid> mid,
+          pending_receiver<interfaces.High> hi,
+          One one
+        );
+      };
+    """
+    self._testThrows('b.mojom', contents,
+                     'RequireContext=.*?kHighest > AllowedContext=.*?kMiddle')
+
+  def testMultipleInterfacesAllowed(self):
+    """Multiple interfaces can be passed, all satisfy the level."""
+    mojoms = self._WriteBasicMojoms()
+
+    b = "b.mojom"
+    self.WriteFile(
+        b, """
+      module b;
+      import "level.mojom";
+      import "interfaces.mojom";
+      struct One { pending_receiver<interfaces.High> hi; };
+      interface PassesMultipleInterfaces {
+        [AllowedContext=level.Level.kHighest]
+        DoMultiple(
+          pending_receiver<interfaces.High> hi,
+          pending_remote<interfaces.Mid> mid,
+          One one
+        );
+      };
+    """)
+    mojoms.append(b)
+    self._ParseAndGenerate(mojoms)
diff --git a/utils/ipc/mojo/public/tools/bindings/concatenate-files.py b/utils/ipc/mojo/public/tools/bindings/concatenate-files.py
index 48bc66fd..4dd26d4a 100755
--- a/utils/ipc/mojo/public/tools/bindings/concatenate-files.py
+++ b/utils/ipc/mojo/public/tools/bindings/concatenate-files.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 2019 The Chromium Authors. All rights reserved.
+# Copyright 2019 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 #
@@ -15,6 +15,7 @@
 from __future__ import print_function
 
 import optparse
+import sys
 
 
 def Concatenate(filenames):
@@ -47,7 +48,7 @@ def main():
   parser.set_usage("""Concatenate several files into one.
       Equivalent to: cat file1 ... > target.""")
   (_options, args) = parser.parse_args()
-  exit(0 if Concatenate(args) else 1)
+  sys.exit(0 if Concatenate(args) else 1)
 
 
 if __name__ == "__main__":
diff --git a/utils/ipc/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py b/utils/ipc/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py
index be8985ce..770081e1 100755
--- a/utils/ipc/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py
+++ b/utils/ipc/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 2018 The Chromium Authors. All rights reserved.
+# Copyright 2018 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -20,6 +20,7 @@ from __future__ import print_function
 
 import optparse
 import re
+import sys
 
 
 _MOJO_INTERNAL_MODULE_NAME = "mojo.internal"
@@ -34,7 +35,7 @@ def FilterLine(filename, line, output):
     match = re.match("goog.provide\('([^']+)'\);", line)
     if not match:
       print("Invalid goog.provide line in %s:\n%s" % (filename, line))
-      exit(1)
+      sys.exit(1)
 
     module_name = match.group(1)
     if module_name == _MOJO_INTERNAL_MODULE_NAME:
@@ -67,7 +68,8 @@ def main():
     Concatenate several files into one, stripping Closure provide and
     require directives along the way.""")
   (_, args) = parser.parse_args()
-  exit(0 if ConcatenateAndReplaceExports(args) else 1)
+  sys.exit(0 if ConcatenateAndReplaceExports(args) else 1)
+
 
 if __name__ == "__main__":
   main()
diff --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
index 8b78d092..c6daff03 100644
--- a/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py
+++ b/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py
@@ -1,4 +1,4 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
+# Copyright 2017 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Generates a list of all files in a directory.
diff --git a/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py b/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py
index a0096649..d73c6ea4 100755
--- a/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py
+++ b/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Generates a JSON typemap from its command-line arguments and dependencies.
@@ -82,6 +82,7 @@ def LoadCppTypemapConfig(path):
       for entry in config['types']:
         configs[entry['mojom']] = {
             'typename': entry['cpp'],
+            'forward_declaration': entry.get('forward_declaration', None),
             'public_headers': config.get('traits_headers', []),
             'traits_headers': config.get('traits_private_headers', []),
             'copyable_pass_by_value': entry.get('copyable_pass_by_value',
diff --git a/utils/ipc/mojo/public/tools/bindings/minify_with_terser.py b/utils/ipc/mojo/public/tools/bindings/minify_with_terser.py
new file mode 100755
index 00000000..cefee7a4
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/bindings/minify_with_terser.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This utility minifies JS files with terser.
+#
+# Instance of 'node' has no 'RunNode' member (no-member)
+# pylint: disable=no-member
+
+import argparse
+import os
+import sys
+
+_HERE_PATH = os.path.dirname(__file__)
+_SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..', '..'))
+_CWD = os.getcwd()
+sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node'))
+import node
+import node_modules
+
+
+def MinifyFile(input_file, output_file):
+  node.RunNode([
+      node_modules.PathToTerser(), input_file, '--mangle', '--compress',
+      '--comments', 'false', '--output', output_file
+  ])
+
+
+def main(argv):
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--input', required=True)
+  parser.add_argument('--output', required=True)
+  args = parser.parse_args(argv)
+
+  # Delete the output file if it already exists. It may be a sym link to the
+  # input, because in non-optimized/pre-Terser builds the input file is copied
+  # to the output location with gn copy().
+  out_path = os.path.join(_CWD, args.output)
+  if (os.path.exists(out_path)):
+    os.remove(out_path)
+
+  MinifyFile(os.path.join(_CWD, args.input), out_path)
+
+
+if __name__ == '__main__':
+  main(sys.argv[1:])
diff --git a/utils/ipc/mojo/public/tools/bindings/mojom.gni b/utils/ipc/mojo/public/tools/bindings/mojom.gni
index fe2a1da3..ded53259 100644
--- a/utils/ipc/mojo/public/tools/bindings/mojom.gni
+++ b/utils/ipc/mojo/public/tools/bindings/mojom.gni
@@ -1,11 +1,11 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/python.gni")
 import("//third_party/closure_compiler/closure_args.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//third_party/protobuf/proto_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
 import("//ui/webui/webui_features.gni")
 
 # TODO(rockot): Maybe we can factor these dependencies out of //mojo. They're
@@ -16,10 +16,12 @@ import("//ui/webui/webui_features.gni")
 import("//build/config/chrome_build.gni")
 import("//build/config/chromecast_build.gni")
 import("//build/config/chromeos/ui_mode.gni")
+import("//build/config/features.gni")
 import("//build/config/nacl/config.gni")
 import("//build/toolchain/kythe.gni")
 import("//components/nacl/features.gni")
 import("//third_party/jinja2/jinja2.gni")
+import("//third_party/ply/ply.gni")
 import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
 declare_args() {
   # Indicates whether typemapping should be supported in this build
@@ -38,17 +40,25 @@ declare_args() {
   # scrambling on all platforms.
   enable_mojom_message_id_scrambling = true
 
+  # Enables generating javascript fuzzing-related code and the bindings for the
+  # MojoLPM fuzzer targets. Off by default.
+  enable_mojom_fuzzer = false
+
   # Enables Closure compilation of generated JS lite bindings. In environments
   # where compilation is supported, any mojom target "foo" will also have a
   # corresponding "foo_js_library_for_compile" target generated.
-  enable_mojom_closure_compile = enable_js_type_check && optimize_webui
-
-  # Enables generating Typescript bindings and compiling them to JS bindings.
-  enable_typescript_bindings = false
+  if (is_chromeos_ash) {
+    enable_mojom_closure_compile = enable_js_type_check && optimize_webui
+  }
+}
 
-  # Enables generating javascript fuzzing-related code and the bindings for the
-  # MojoLPM fuzzer targets. Off by default.
-  enable_mojom_fuzzer = false
+# Closure libraries are needed for mojom_closure_compile, and when
+# js_type_check is enabled on Ash.
+if (is_chromeos_ash) {
+  generate_mojom_closure_libraries =
+      enable_mojom_closure_compile || enable_js_type_check
+} else {
+  generate_mojom_closure_libraries = false
 }
 
 # NOTE: We would like to avoid scrambling message IDs where it doesn't add
@@ -69,9 +79,8 @@ declare_args() {
 # lacros-chrome switches to target_os="chromeos"
 enable_scrambled_message_ids =
     enable_mojom_message_id_scrambling &&
-    (is_mac || is_win ||
-     (is_linux && !is_chromeos_ash && !is_chromecast && !is_chromeos_lacros) ||
-     ((enable_nacl || is_nacl || is_nacl_nonsfi) &&
+    (is_mac || is_win || (is_linux && !is_castos) ||
+     ((enable_nacl || is_nacl) &&
       (target_os != "chromeos" && !chromeos_is_browser_only)))
 
 _mojom_tools_root = "//mojo/public/tools"
@@ -80,7 +89,9 @@ mojom_parser_script = "$_mojom_tools_root/mojom/mojom_parser.py"
 mojom_parser_sources = [
   "$_mojom_library_root/__init__.py",
   "$_mojom_library_root/error.py",
+  "$_mojom_library_root/fileutil.py",
   "$_mojom_library_root/generate/__init__.py",
+  "$_mojom_library_root/generate/check.py",
   "$_mojom_library_root/generate/generator.py",
   "$_mojom_library_root/generate/module.py",
   "$_mojom_library_root/generate/pack.py",
@@ -88,20 +99,28 @@ mojom_parser_sources = [
   "$_mojom_library_root/generate/translate.py",
   "$_mojom_library_root/parse/__init__.py",
   "$_mojom_library_root/parse/ast.py",
+  "$_mojom_library_root/parse/conditional_features.py",
   "$_mojom_library_root/parse/lexer.py",
   "$_mojom_library_root/parse/parser.py",
+  "//tools/diagnosis/crbug_1001171.py",
 ]
 
 mojom_generator_root = "$_mojom_tools_root/bindings"
 mojom_generator_script = "$mojom_generator_root/mojom_bindings_generator.py"
 mojom_generator_sources =
     mojom_parser_sources + [
+      "$mojom_generator_root/checks/__init__.py",
+      "$mojom_generator_root/checks/mojom_attributes_check.py",
+      "$mojom_generator_root/checks/mojom_definitions_check.py",
+      "$mojom_generator_root/checks/mojom_restrictions_check.py",
+      "$mojom_generator_root/generators/__init__.py",
       "$mojom_generator_root/generators/cpp_util.py",
       "$mojom_generator_root/generators/mojom_cpp_generator.py",
       "$mojom_generator_root/generators/mojom_java_generator.py",
-      "$mojom_generator_root/generators/mojom_mojolpm_generator.py",
       "$mojom_generator_root/generators/mojom_js_generator.py",
+      "$mojom_generator_root/generators/mojom_mojolpm_generator.py",
       "$mojom_generator_root/generators/mojom_ts_generator.py",
+      "$mojom_generator_root/generators/mojom_webui_js_bridge_generator.py",
       "$mojom_generator_script",
     ]
 
@@ -243,12 +262,16 @@ if (enable_scrambled_message_ids) {
 #       |cpp_only| is set to true, it overrides this to prevent generation of
 #       Java bindings.
 #
-#   enable_fuzzing (optional)
+#   enable_js_fuzzing (optional)
+#       Enables generation of javascript fuzzing sources for the target if the
+#       global build arg |enable_mojom_fuzzer| is also set to |true|.
+#       Defaults to |true|. If JS fuzzing generation is enabled for a target,
+#       the target will always generate JS bindings even if |cpp_only| is set to
+#       |true|. See note above.
+#
+#   enable_mojolpm_fuzzing (optional)
 #       Enables generation of fuzzing sources for the target if the global build
-#       arg |enable_mojom_fuzzer| is also set to |true|. Defaults to |true|. If
-#       fuzzing generation is enabled for a target, the target will always
-#       generate JS bindings even if |cpp_only| is set to |true|. See note
-#       above.
+#       arg |enable_mojom_fuzzer| is also set to |true|. Defaults to |true|.
 #
 #   support_lazy_serialization (optional)
 #       If set to |true|, generated C++ bindings will effectively prefer to
@@ -313,6 +336,16 @@ if (enable_scrambled_message_ids) {
 #   use_typescript_sources (optional)
 #       Uses the Typescript generator to generate JavaScript bindings.
 #
+#   generate_legacy_js_bindings (optional)
+#       Generate js_data_deps target containing legacy JavaScript bindings files
+#       for Blink tests and other non-WebUI users when generating TypeScript
+#       bindings for WebUI. Ignored if use_typescript_sources is not set to
+#       true.
+#
+#   webui_js_bridge_config (optional)
+#       Prefer to use the `mojom_with_webui_js_bridge` target below instead
+#       of using this argument directly.
+#
 #   js_generate_struct_deserializers (optional)
 #       Generates JS deerialize methods for structs.
 #
@@ -402,6 +435,12 @@ if (enable_scrambled_message_ids) {
 #             should be mapped in generated bindings. This is a string like
 #             "::base::Value" or "std::vector<::base::Value>".
 #
+#         forward_declaration (optional)
+#             A forward declaration of the C++ type, which bindings that don't
+#             need the full type definition can use to reduce the size of
+#             the generated code. This is a string like
+#             "namespace base { class Value; }".
+#
 #         move_only (optional)
 #             A boolean value (default false) which indicates whether the C++
 #             type is move-only. If true, generated bindings will pass the type
@@ -621,20 +660,26 @@ template("mojom") {
   build_metadata_filename = "$target_gen_dir/$target_name.build_metadata"
   build_metadata = {
   }
-  build_metadata.sources = rebase_path(sources_list)
+  build_metadata.sources = rebase_path(sources_list, target_gen_dir)
   build_metadata.deps = []
   foreach(dep, all_deps) {
     dep_target_gen_dir = get_label_info(dep, "target_gen_dir")
     dep_name = get_label_info(dep, "name")
     build_metadata.deps +=
-        [ rebase_path("$dep_target_gen_dir/$dep_name.build_metadata") ]
+        [ rebase_path("$dep_target_gen_dir/$dep_name.build_metadata",
+                      target_gen_dir) ]
   }
   write_file(build_metadata_filename, build_metadata, "json")
 
-  generate_fuzzing =
-      (!defined(invoker.enable_fuzzing) || invoker.enable_fuzzing) &&
+  generate_js_fuzzing =
+      (!defined(invoker.enable_js_fuzzing) || invoker.enable_js_fuzzing) &&
       enable_mojom_fuzzer && (!defined(invoker.testonly) || !invoker.testonly)
 
+  generate_mojolpm_fuzzing =
+      (!defined(invoker.enable_mojolpm_fuzzing) ||
+       invoker.enable_mojolpm_fuzzing) && enable_mojom_fuzzer &&
+      (!defined(invoker.testonly) || !invoker.testonly)
+
   parser_target_name = "${target_name}__parser"
   parser_deps = []
   foreach(dep, all_deps) {
@@ -683,12 +728,17 @@ template("mojom") {
       enabled_features += [ "is_win" ]
     }
 
-    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-    python2_action(parser_target_name) {
+    if (is_apple) {
+      enabled_features += [ "is_apple" ]
+    }
+
+    action(parser_target_name) {
+      allow_remote = true
+      custom_processor = "mojom_parser"
       script = mojom_parser_script
-      inputs = mojom_parser_sources + [ build_metadata_filename ]
+      inputs = mojom_parser_sources + ply_sources + [ build_metadata_filename ]
       sources = sources_list
-      deps = parser_deps
+      public_deps = parser_deps
       outputs = []
       foreach(base_path, output_file_base_paths) {
         filename = get_path_info(base_path, "file")
@@ -698,31 +748,35 @@ template("mojom") {
 
       filelist = []
       foreach(source, sources_list) {
-        filelist += [ rebase_path(source) ]
+        filelist += [ rebase_path(source, root_build_dir) ]
       }
-      response_file_contents = filelist
+
+      # Workaround for https://github.com/ninja-build/ninja/issues/1966.
+      rsp_file = "$target_gen_dir/${target_name}.rsp"
+      write_file(rsp_file, filelist)
+      inputs += [ rsp_file ]
 
       args = [
         # Resolve relative input mojom paths against both the root src dir and
         # the root gen dir.
         "--input-root",
-        rebase_path("//."),
+        rebase_path("//.", root_build_dir),
         "--input-root",
-        rebase_path(root_gen_dir),
+        rebase_path(root_gen_dir, root_build_dir),
 
         "--output-root",
-        rebase_path(root_gen_dir),
+        rebase_path(root_gen_dir, root_build_dir),
 
-        "--mojom-file-list={{response_file_name}}",
+        "--mojom-file-list=" + rebase_path(rsp_file, root_build_dir),
 
         "--check-imports",
-        rebase_path(build_metadata_filename),
+        rebase_path(build_metadata_filename, root_build_dir),
       ]
 
       if (defined(invoker.input_root_override)) {
         args += [
           "--input-root",
-          rebase_path(invoker.input_root_override),
+          rebase_path(invoker.input_root_override, root_build_dir),
         ]
       }
 
@@ -738,6 +792,9 @@ template("mojom") {
           "--add-module-metadata",
           "webui_module_path=${invoker.webui_module_path}",
         ]
+        if (defined(invoker.generate_legacy_js_bindings)) {
+          args += [ "legacy_js_only=${invoker.generate_legacy_js_bindings}" ]
+        }
       }
     }
   }
@@ -819,11 +876,12 @@ template("mojom") {
       }
     }
 
-    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-    python2_action(generator_cpp_message_ids_target_name) {
+    action(generator_cpp_message_ids_target_name) {
+      allow_remote = true
       script = mojom_generator_script
       inputs = mojom_generator_sources + jinja2_sources
-      sources = sources_list
+      sources = sources_list +
+                [ "$root_gen_dir/mojo/public/tools/bindings/cpp_templates.zip" ]
       deps = [
         ":$parser_target_name",
         "//mojo/public/tools/bindings:precompile_templates",
@@ -835,16 +893,22 @@ template("mojom") {
       args = common_generator_args
       filelist = []
       foreach(source, sources_list) {
-        filelist += [ rebase_path("$source", root_build_dir) ]
+        filelist += [ rebase_path(source, root_build_dir) ]
       }
       foreach(base_path, output_file_base_paths) {
+        filename = get_path_info(base_path, "file")
+        dirname = get_path_info(base_path, "dir")
+        inputs += [ "$root_gen_dir/$dirname/${filename}-module" ]
         outputs += [ "$root_gen_dir/$base_path-shared-message-ids.h" ]
       }
 
-      response_file_contents = filelist
+      # Workaround for https://github.com/ninja-build/ninja/issues/1966.
+      rsp_file = "$target_gen_dir/${target_name}.rsp"
+      write_file(rsp_file, filelist)
+      inputs += [ rsp_file ]
 
       args += [
-        "--filelist={{response_file_name}}",
+        "--filelist=" + rebase_path(rsp_file, root_build_dir),
         "--generate_non_variant_code",
         "--generate_message_ids",
         "-g",
@@ -860,12 +924,13 @@ template("mojom") {
 
     generator_shared_target_name = "${target_name}_shared__generator"
 
-    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-    python2_action(generator_shared_target_name) {
+    action(generator_shared_target_name) {
+      allow_remote = true
       visibility = [ ":*" ]
       script = mojom_generator_script
       inputs = mojom_generator_sources + jinja2_sources
-      sources = sources_list
+      sources = sources_list +
+                [ "$root_gen_dir/mojo/public/tools/bindings/cpp_templates.zip" ]
       deps = [
         ":$parser_target_name",
         "//mojo/public/tools/bindings:precompile_templates",
@@ -878,9 +943,14 @@ template("mojom") {
       args = common_generator_args
       filelist = []
       foreach(source, sources_list) {
-        filelist += [ rebase_path("$source", root_build_dir) ]
+        filelist += [ rebase_path(source, root_build_dir) ]
       }
       foreach(base_path, output_file_base_paths) {
+        # Need the mojom-module as an input to this action.
+        filename = get_path_info(base_path, "file")
+        dirname = get_path_info(base_path, "dir")
+        inputs += [ "$root_gen_dir/$dirname/${filename}-module" ]
+
         outputs += [
           "$root_gen_dir/$base_path-params-data.h",
           "$root_gen_dir/$base_path-shared-internal.h",
@@ -889,10 +959,13 @@ template("mojom") {
         ]
       }
 
-      response_file_contents = filelist
+      # Workaround for https://github.com/ninja-build/ninja/issues/1966.
+      rsp_file = "$target_gen_dir/${target_name}.rsp"
+      write_file(rsp_file, filelist)
+      inputs += [ rsp_file ]
 
       args += [
-        "--filelist={{response_file_name}}",
+        "--filelist=" + rebase_path(rsp_file, root_build_dir),
         "--generate_non_variant_code",
         "-g",
         "c++",
@@ -972,7 +1045,7 @@ template("mojom") {
     }
   }
 
-  if (generate_fuzzing) {
+  if (generate_mojolpm_fuzzing) {
     # This block generates the proto files used for the MojoLPM fuzzer,
     # and the corresponding proto targets that will be linked in the fuzzer
     # targets. These are independent of the typemappings, and can be done
@@ -981,11 +1054,15 @@ template("mojom") {
     generator_mojolpm_proto_target_name =
         "${target_name}_mojolpm_proto_generator"
 
-    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-    python2_action(generator_mojolpm_proto_target_name) {
+    action(generator_mojolpm_proto_target_name) {
+      allow_remote = true
       script = mojom_generator_script
       inputs = mojom_generator_sources + jinja2_sources
-      sources = invoker.sources
+      sources =
+          invoker.sources + [
+            "$root_gen_dir/mojo/public/tools/bindings/cpp_templates.zip",
+            "$root_gen_dir/mojo/public/tools/bindings/mojolpm_templates.zip",
+          ]
       deps = [
         ":$parser_target_name",
         "//mojo/public/tools/bindings:precompile_templates",
@@ -994,15 +1071,37 @@ template("mojom") {
       outputs = []
       args = common_generator_args
       filelist = []
-      foreach(source, invoker.sources) {
-        filelist += [ rebase_path("$source", root_build_dir) ]
+
+      # Split the input into generated and non-generated source files. They
+      # need to be processed separately.
+      gen_dir_path_wildcard = get_path_info("//", "gen_dir") + "/*"
+      non_gen_sources =
+          filter_exclude(invoker.sources, [ gen_dir_path_wildcard ])
+      gen_sources = filter_include(invoker.sources, [ gen_dir_path_wildcard ])
+
+      foreach(source, non_gen_sources) {
+        filelist += [ rebase_path(source, root_build_dir) ]
+        inputs += [ "$target_gen_dir/$source-module" ]
         outputs += [ "$target_gen_dir/$source.mojolpm.proto" ]
       }
 
-      response_file_contents = filelist
+      foreach(source, gen_sources) {
+        filelist += [ rebase_path(source, root_build_dir) ]
+
+        # For generated files, we assume they're in the target_gen_dir or a
+        # sub-folder of it. Rebase the path so we can get the relative location.
+        source_file = rebase_path(source, target_gen_dir)
+        inputs += [ "$target_gen_dir/$source_file-module" ]
+        outputs += [ "$target_gen_dir/$source_file.mojolpm.proto" ]
+      }
+
+      # Workaround for https://github.com/ninja-build/ninja/issues/1966.
+      rsp_file = "$target_gen_dir/${target_name}.rsp"
+      write_file(rsp_file, filelist)
+      inputs += [ rsp_file ]
 
       args += [
-        "--filelist={{response_file_name}}",
+        "--filelist=" + rebase_path(rsp_file, root_build_dir),
         "--generate_non_variant_code",
         "-g",
         "mojolpm",
@@ -1014,9 +1113,20 @@ template("mojom") {
       proto_library(mojolpm_proto_target_name) {
         testonly = true
         generate_python = false
+
+        # Split the input into generated and non-generated source files. They
+        # need to be processed separately.
+        gen_dir_path_wildcard = get_path_info("//", "gen_dir") + "/*"
+        non_gen_sources =
+            filter_exclude(invoker.sources, [ gen_dir_path_wildcard ])
+        gen_sources = filter_include(invoker.sources, [ gen_dir_path_wildcard ])
         sources = process_file_template(
-                invoker.sources,
+                non_gen_sources,
                 [ "{{source_gen_dir}}/{{source_file_part}}.mojolpm.proto" ])
+        sources += process_file_template(
+                gen_sources,
+                [ "{{source_dir}}/{{source_file_part}}.mojolpm.proto" ])
+
         import_dirs = [ "//" ]
         proto_in_dir = "${root_gen_dir}"
         proto_out_dir = "."
@@ -1055,7 +1165,7 @@ template("mojom") {
     component_macro_suffix = ""
   }
   if ((!defined(invoker.disable_variants) || !invoker.disable_variants) &&
-      !is_ios) {
+      use_blink) {
     blink_variant = {
       variant = "blink"
       component_macro_suffix = "_BLINK"
@@ -1149,39 +1259,6 @@ template("mojom") {
             "${bindings_configuration.component_macro_suffix}_IMPL" ]
     }
 
-    export_args = []
-    export_args_overridden = false
-    if (defined(bindings_configuration.for_blink) &&
-        bindings_configuration.for_blink) {
-      if (defined(invoker.export_class_attribute_blink)) {
-        export_args_overridden = true
-        export_args += [
-          "--export_attribute",
-          invoker.export_class_attribute_blink,
-          "--export_header",
-          invoker.export_header_blink,
-        ]
-      }
-    } else if (defined(invoker.export_class_attribute)) {
-      export_args_overridden = true
-      export_args += [
-        "--export_attribute",
-        invoker.export_class_attribute,
-        "--export_header",
-        invoker.export_header,
-      ]
-    }
-
-    if (!export_args_overridden && defined(invoker.component_macro_prefix)) {
-      export_args += [
-        "--export_attribute",
-        "COMPONENT_EXPORT(${invoker.component_macro_prefix}" +
-            "${bindings_configuration.component_macro_suffix})",
-        "--export_header",
-        "base/component_export.h",
-      ]
-    }
-
     generate_java = false
     if (!cpp_only && defined(invoker.generate_java)) {
       generate_java = invoker.generate_java
@@ -1190,6 +1267,38 @@ template("mojom") {
     type_mappings_path =
         "$target_gen_dir/${target_name}${variant_suffix}__type_mappings"
     if (sources_list != []) {
+      export_args = []
+      export_args_overridden = false
+      if (defined(bindings_configuration.for_blink) &&
+          bindings_configuration.for_blink) {
+        if (defined(invoker.export_class_attribute_blink)) {
+          export_args_overridden = true
+          export_args += [
+            "--export_attribute",
+            invoker.export_class_attribute_blink,
+            "--export_header",
+            invoker.export_header_blink,
+          ]
+        }
+      } else if (defined(invoker.export_class_attribute)) {
+        export_args_overridden = true
+        export_args += [
+          "--export_attribute",
+          invoker.export_class_attribute,
+          "--export_header",
+          invoker.export_header,
+        ]
+      }
+      if (!export_args_overridden && defined(invoker.component_macro_prefix)) {
+        export_args += [
+          "--export_attribute",
+          "COMPONENT_EXPORT(${invoker.component_macro_prefix}" +
+              "${bindings_configuration.component_macro_suffix})",
+          "--export_header",
+          "base/component_export.h",
+        ]
+      }
+
       generator_cpp_output_suffixes = []
       variant_dash_suffix = ""
       if (defined(variant)) {
@@ -1198,7 +1307,6 @@ template("mojom") {
       generator_cpp_output_suffixes += [
         "${variant_dash_suffix}-forward.h",
         "${variant_dash_suffix}-import-headers.h",
-        "${variant_dash_suffix}-test-utils.cc",
         "${variant_dash_suffix}-test-utils.h",
         "${variant_dash_suffix}.cc",
         "${variant_dash_suffix}.h",
@@ -1207,16 +1315,28 @@ template("mojom") {
       generator_target_name = "${target_name}${variant_suffix}__generator"
 
       # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-      python2_action(generator_target_name) {
+      action(generator_target_name) {
+        allow_remote = true
         visibility = [ ":*" ]
         script = mojom_generator_script
         inputs = mojom_generator_sources + jinja2_sources
-        sources = sources_list
+        sources =
+            sources_list + [
+              "$root_gen_dir/mojo/public/tools/bindings/cpp_templates.zip",
+              type_mappings_path,
+            ]
+        if (generate_mojolpm_fuzzing &&
+            !defined(bindings_configuration.variant)) {
+          sources += [
+            "$root_gen_dir/mojo/public/tools/bindings/mojolpm_templates.zip",
+          ]
+        }
         deps = [
           ":$parser_target_name",
           ":$type_mappings_target_name",
           "//mojo/public/tools/bindings:precompile_templates",
         ]
+
         if (defined(invoker.parser_deps)) {
           deps += invoker.parser_deps
         }
@@ -1224,18 +1344,22 @@ template("mojom") {
         args = common_generator_args + export_args
         filelist = []
         foreach(source, sources_list) {
-          filelist += [ rebase_path("$source", root_build_dir) ]
+          filelist += [ rebase_path(source, root_build_dir) ]
         }
         foreach(base_path, output_file_base_paths) {
+          filename = get_path_info(base_path, "file")
+          dirname = get_path_info(base_path, "dir")
+          inputs += [ "$root_gen_dir/$dirname/${filename}-module" ]
+
           outputs += [
             "$root_gen_dir/${base_path}${variant_dash_suffix}-forward.h",
             "$root_gen_dir/${base_path}${variant_dash_suffix}-import-headers.h",
-            "$root_gen_dir/${base_path}${variant_dash_suffix}-test-utils.cc",
             "$root_gen_dir/${base_path}${variant_dash_suffix}-test-utils.h",
             "$root_gen_dir/${base_path}${variant_dash_suffix}.cc",
             "$root_gen_dir/${base_path}${variant_dash_suffix}.h",
           ]
-          if (generate_fuzzing && !defined(bindings_configuration.variant)) {
+          if (generate_mojolpm_fuzzing &&
+              !defined(bindings_configuration.variant)) {
             outputs += [
               "$root_gen_dir/${base_path}${variant_dash_suffix}-mojolpm.cc",
               "$root_gen_dir/${base_path}${variant_dash_suffix}-mojolpm.h",
@@ -1243,14 +1367,17 @@ template("mojom") {
           }
         }
 
-        response_file_contents = filelist
-
+        # Workaround for https://github.com/ninja-build/ninja/issues/1966.
+        rsp_file = "$target_gen_dir/${target_name}.rsp"
+        write_file(rsp_file, filelist)
+        inputs += [ rsp_file ]
         args += [
-          "--filelist={{response_file_name}}",
+          "--filelist=" + rebase_path("$rsp_file", root_build_dir),
           "-g",
         ]
 
-        if (generate_fuzzing && !defined(bindings_configuration.variant)) {
+        if (generate_mojolpm_fuzzing &&
+            !defined(bindings_configuration.variant)) {
           args += [ "c++,mojolpm" ]
         } else {
           args += [ "c++" ]
@@ -1294,6 +1421,8 @@ template("mojom") {
               "--extra_cpp_template_paths",
               rebase_path(extra_cpp_template, root_build_dir),
             ]
+            inputs += [ extra_cpp_template ]
+
             assert(
                 get_path_info(extra_cpp_template, "extension") == "tmpl",
                 "--extra_cpp_template_paths only accepts template files ending in extension .tmpl")
@@ -1306,62 +1435,6 @@ template("mojom") {
       }
     }
 
-    if (generate_fuzzing && !defined(variant)) {
-      # This block contains the C++ targets for the MojoLPM fuzzer, we need to
-      # do this here so that we can use the typemap configuration for the
-      # empty-variant Mojo target.
-
-      mojolpm_target_name = "${target_name}_mojolpm"
-      mojolpm_generator_target_name = "${target_name}__generator"
-      source_set(mojolpm_target_name) {
-        # There are still a few missing header dependencies between mojo targets
-        # with typemaps and the dependencies of their typemap headers. It would
-        # be good to enable include checking for these in the future though.
-        check_includes = false
-        testonly = true
-        if (defined(invoker.sources)) {
-          sources = process_file_template(
-                  invoker.sources,
-                  [
-                    "{{source_gen_dir}}/{{source_file_part}}-mojolpm.cc",
-                    "{{source_gen_dir}}/{{source_file_part}}-mojolpm.h",
-                  ])
-          deps = []
-        } else {
-          sources = []
-          deps = []
-        }
-
-        public_deps = [
-          ":$generator_shared_target_name",
-
-          # NB: hardcoded dependency on the no-variant variant generator, since
-          # mojolpm only uses the no-variant type.
-          ":$mojolpm_generator_target_name",
-          ":$mojolpm_proto_target_name",
-          "//base",
-          "//mojo/public/tools/fuzzers:mojolpm",
-        ]
-
-        foreach(d, all_deps) {
-          # Resolve the name, so that a target //mojo/something becomes
-          # //mojo/something:something and we can append variant_suffix to
-          # get the cpp dependency name.
-          full_name = get_label_info("$d", "label_no_toolchain")
-          public_deps += [ "${full_name}_mojolpm" ]
-        }
-
-        foreach(config, cpp_typemap_configs) {
-          if (defined(config.traits_deps)) {
-            deps += config.traits_deps
-          }
-          if (defined(config.traits_public_deps)) {
-            public_deps += config.traits_public_deps
-          }
-        }
-      }
-    }
-
     # Write the typemapping configuration for this target out to a file to be
     # validated by a Python script. This helps catch mistakes that can't
     # be caught by logic in GN.
@@ -1389,20 +1462,20 @@ template("mojom") {
     write_file(_typemap_config_filename, _rebased_typemap_configs, "json")
     _mojom_target_name = target_name
 
-    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-    python2_action(_typemap_validator_target_name) {
+    action(_typemap_validator_target_name) {
+      allow_remote = true
       script = "$mojom_generator_root/validate_typemap_config.py"
       inputs = [ _typemap_config_filename ]
       outputs = [ _typemap_stamp_filename ]
       args = [
         get_label_info(_mojom_target_name, "label_no_toolchain"),
-        rebase_path(_typemap_config_filename),
-        rebase_path(_typemap_stamp_filename),
+        rebase_path(_typemap_config_filename, root_build_dir),
+        rebase_path(_typemap_stamp_filename, root_build_dir),
       ]
     }
 
-    # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-    python2_action(type_mappings_target_name) {
+    action(type_mappings_target_name) {
+      allow_remote = true
       inputs =
           mojom_generator_sources + jinja2_sources + [ _typemap_stamp_filename ]
       outputs = [ type_mappings_path ]
@@ -1413,6 +1486,7 @@ template("mojom") {
         rebase_path(type_mappings_path, root_build_dir),
       ]
 
+      sources = []
       foreach(d, all_deps) {
         name = get_label_info(d, "label_no_toolchain")
         toolchain = get_label_info(d, "toolchain")
@@ -1422,12 +1496,11 @@ template("mojom") {
         dependency_output_dir =
             get_label_info(dependency_output, "target_gen_dir")
         dependency_name = get_label_info(dependency_output, "name")
-        dependency_path =
-            rebase_path("$dependency_output_dir/${dependency_name}",
-                        root_build_dir)
+        dependency_path = "$dependency_output_dir/${dependency_name}"
+        sources += [ dependency_path ]
         args += [
           "--dependency",
-          dependency_path,
+          rebase_path(dependency_path, root_build_dir),
         ]
       }
 
@@ -1485,11 +1558,15 @@ template("mojom") {
       if (defined(output_name_override)) {
         output_name = output_name_override
       }
-      visibility = output_visibility + [ ":$output_target_name" ]
+      visibility = output_visibility + [
+                     ":$output_target_name",
+                     ":${target_name}_mojolpm",
+                   ]
       if (defined(invoker.testonly)) {
         testonly = invoker.testonly
       }
       defines = export_defines
+      configs += [ "//build/config/compiler:wexit_time_destructors" ]
       configs += extra_configs
       if (output_file_base_paths != []) {
         sources = []
@@ -1578,13 +1655,81 @@ template("mojom") {
       }
     }
 
+    if (generate_mojolpm_fuzzing && !defined(variant)) {
+      # This block contains the C++ targets for the MojoLPM fuzzer, we need to
+      # do this here so that we can use the typemap configuration for the
+      # empty-variant Mojo target.
+
+      mojolpm_target_name = "${target_name}_mojolpm"
+      mojolpm_generator_target_name = "${target_name}__generator"
+      source_set(mojolpm_target_name) {
+        # There are still a few missing header dependencies between mojo targets
+        # with typemaps and the dependencies of their typemap headers. It would
+        # be good to enable include checking for these in the future though.
+        check_includes = false
+        testonly = true
+        if (defined(invoker.sources)) {
+          # Split the input into generated and non-generated source files. They
+          # need to be processed separately.
+          gen_dir_path_wildcard = get_path_info("//", "gen_dir") + "/*"
+          non_gen_sources =
+              filter_exclude(invoker.sources, [ gen_dir_path_wildcard ])
+          gen_sources =
+              filter_include(invoker.sources, [ gen_dir_path_wildcard ])
+          sources = process_file_template(
+                  non_gen_sources,
+                  [
+                    "{{source_gen_dir}}/{{source_file_part}}-mojolpm.cc",
+                    "{{source_gen_dir}}/{{source_file_part}}-mojolpm.h",
+                  ])
+          sources += process_file_template(
+                  gen_sources,
+                  [
+                    "{{source_dir}}/{{source_file_part}}-mojolpm.cc",
+                    "{{source_dir}}/{{source_file_part}}-mojolpm.h",
+                  ])
+          deps = [ ":$output_target_name" ]
+        } else {
+          sources = []
+          deps = []
+        }
+
+        public_deps = [
+          ":$generator_shared_target_name",
+
+          # NB: hardcoded dependency on the no-variant variant generator, since
+          # mojolpm only uses the no-variant type.
+          ":$mojolpm_generator_target_name",
+          ":$mojolpm_proto_target_name",
+          "//base",
+          "//mojo/public/tools/fuzzers:mojolpm",
+        ]
+
+        foreach(d, all_deps) {
+          # Resolve the name, so that a target //mojo/something becomes
+          # //mojo/something:something and we can append variant_suffix to
+          # get the cpp dependency name.
+          full_name = get_label_info("$d", "label_no_toolchain")
+          public_deps += [ "${full_name}_mojolpm" ]
+        }
+
+        foreach(config, cpp_typemap_configs) {
+          if (defined(config.traits_deps)) {
+            deps += config.traits_deps
+          }
+          if (defined(config.traits_public_deps)) {
+            public_deps += config.traits_public_deps
+          }
+        }
+      }
+    }
+
     if (generate_java && is_android) {
       import("//build/config/android/rules.gni")
 
       java_generator_target_name = target_name + "_java__generator"
       if (sources_list != []) {
-        # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-        python2_action(java_generator_target_name) {
+        action(java_generator_target_name) {
           script = mojom_generator_script
           inputs = mojom_generator_sources + jinja2_sources
           sources = sources_list
@@ -1597,7 +1742,7 @@ template("mojom") {
           args = common_generator_args
           filelist = []
           foreach(source, sources_list) {
-            filelist += [ rebase_path("$source", root_build_dir) ]
+            filelist += [ rebase_path(source, root_build_dir) ]
           }
           foreach(base_path, output_file_base_paths) {
             outputs += [ "$root_gen_dir/$base_path.srcjar" ]
@@ -1624,8 +1769,7 @@ template("mojom") {
 
       java_srcjar_target_name = target_name + "_java_sources"
 
-      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-      python2_action(java_srcjar_target_name) {
+      action(java_srcjar_target_name) {
         script = "//build/android/gyp/zip.py"
         inputs = []
         if (output_file_base_paths != []) {
@@ -1651,7 +1795,6 @@ template("mojom") {
       android_library(java_target_name) {
         forward_variables_from(invoker, [ "enable_bytecode_checks" ])
         deps = [
-          "//base:base_java",
           "//mojo/public/java:bindings_java",
           "//mojo/public/java:system_java",
           "//third_party/androidx:androidx_annotation_annotation_java",
@@ -1673,21 +1816,103 @@ template("mojom") {
     }
   }
 
+  if (defined(invoker.webui_js_bridge_config)) {
+    if (sources_list != []) {
+      bridge_config = invoker.webui_js_bridge_config
+
+      generator_webui_js_bridge_impl_target_name =
+          "${target_name}_webui_js_bridge_impl__generator"
+      action(generator_webui_js_bridge_impl_target_name) {
+        visibility = [ ":*" ]
+        script = mojom_generator_script
+        inputs = mojom_generator_sources + jinja2_sources
+        sources = sources_list
+
+        deps = [
+          ":$parser_target_name",
+          "//mojo/public/tools/bindings:precompile_templates",
+        ]
+
+        filelist = []
+        foreach(source, sources_list) {
+          filelist += [ rebase_path(source, root_build_dir) ]
+        }
+
+        outputs = []
+        foreach(base_path, output_file_base_paths) {
+          outputs += [
+            "$root_gen_dir/${base_path}-webui-js-bridge-impl.cc",
+            "$root_gen_dir/${base_path}-webui-js-bridge-impl.h",
+          ]
+        }
+
+        response_file_contents = filelist
+
+        args = common_generator_args + export_args
+        args += [
+          "--filelist={{response_file_name}}",
+          "-g",
+          "webui_js_bridge",
+        ]
+
+        rebased_webui_controller_header =
+            rebase_path(bridge_config.webui_controller_header, "//")
+
+        # Use a single string for the argument to avoid it being parsed as a
+        # filename by the mojom_bindings_generator.py argument parser.
+        config_arg =
+            "--webui_js_bridge_config=" + bridge_config.webui_controller + "=" +
+            rebased_webui_controller_header
+
+        args += [ config_arg ]
+      }
+
+      cpp_target_name = target_name
+      source_set("${target_name}_webui_js_bridge_impl") {
+        if (defined(invoker.testonly)) {
+          testonly = invoker.testonly
+        }
+
+        sources = []
+        foreach(base_path, output_file_base_paths) {
+          sources += [
+            "$root_gen_dir/${base_path}-webui-js-bridge-impl.cc",
+            "$root_gen_dir/${base_path}-webui-js-bridge-impl.h",
+          ]
+        }
+        deps = [
+          ":$cpp_target_name",
+          ":$generator_webui_js_bridge_impl_target_name",
+        ]
+
+        deps += bridge_config.webui_controller_deps
+      }
+    }
+  }
+
   use_typescript_for_target =
-      enable_typescript_bindings && defined(invoker.use_typescript_sources) &&
-      invoker.use_typescript_sources
+      defined(invoker.use_typescript_sources) &&
+      invoker.use_typescript_sources && defined(invoker.webui_module_path)
 
   if (!use_typescript_for_target && defined(invoker.use_typescript_sources)) {
     not_needed(invoker, [ "use_typescript_sources" ])
   }
 
-  if ((generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) &&
-      !use_typescript_for_target) {
+  generate_legacy_js = !use_typescript_for_target ||
+                       (defined(invoker.generate_legacy_js_bindings) &&
+                        invoker.generate_legacy_js_bindings)
+
+  # Targets needed by both TS and JS bindings targets. These are needed
+  # unconditionally for JS bindings targets, and are needed for TS bindings
+  # targets when generate_legacy_js_bindings is true. This option is provided
+  # since the legacy bindings are needed by Blink tests and non-Chromium users,
+  # which are not expected to migrate to modules or TypeScript.
+  if (generate_legacy_js && (generate_js_fuzzing ||
+                             !defined(invoker.cpp_only) || !invoker.cpp_only)) {
     if (sources_list != []) {
       generator_js_target_name = "${target_name}_js__generator"
 
-      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-      python2_action(generator_js_target_name) {
+      action(generator_js_target_name) {
         script = mojom_generator_script
         inputs = mojom_generator_sources + jinja2_sources
         sources = sources_list
@@ -1702,19 +1927,18 @@ template("mojom") {
         args = common_generator_args
         filelist = []
         foreach(source, sources_list) {
-          filelist += [ rebase_path("$source", root_build_dir) ]
+          filelist += [ rebase_path(source, root_build_dir) ]
         }
         foreach(base_path, output_file_base_paths) {
           outputs += [
             "$root_gen_dir/$base_path.js",
-            "$root_gen_dir/$base_path.externs.js",
             "$root_gen_dir/$base_path.m.js",
             "$root_gen_dir/$base_path-lite.js",
-            "$root_gen_dir/$base_path.html",
             "$root_gen_dir/$base_path-lite-for-compile.js",
           ]
 
-          if (defined(invoker.webui_module_path)) {
+          if (defined(invoker.webui_module_path) &&
+              !use_typescript_for_target) {
             outputs += [ "$root_gen_dir/mojom-webui/$base_path-webui.js" ]
           }
         }
@@ -1725,7 +1949,6 @@ template("mojom") {
           "--filelist={{response_file_name}}",
           "-g",
           "javascript",
-          "--js_bindings_mode=new",
         ]
 
         if (defined(invoker.js_generate_struct_deserializers) &&
@@ -1739,7 +1962,7 @@ template("mojom") {
           args += message_scrambling_args
         }
 
-        if (generate_fuzzing) {
+        if (generate_js_fuzzing) {
           args += [ "--generate_fuzzing" ]
         }
       }
@@ -1783,31 +2006,13 @@ template("mojom") {
         data_deps += [ "${full_name}_js_data_deps" ]
       }
     }
+  }
 
-    js_library_target_name = "${target_name}_js_library"
-    if (sources_list != []) {
-      js_library(js_library_target_name) {
-        extra_public_deps = [ ":$generator_js_target_name" ]
-        sources = []
-        foreach(base_path, output_file_base_paths) {
-          sources += [ "$root_gen_dir/${base_path}-lite.js" ]
-        }
-        externs_list = [
-          "${externs_path}/mojo_core.js",
-          "${externs_path}/pending.js",
-        ]
-
-        deps = []
-        foreach(d, all_deps) {
-          full_name = get_label_info(d, "label_no_toolchain")
-          deps += [ "${full_name}_js_library" ]
-        }
-      }
-    } else {
-      group(js_library_target_name) {
-      }
-    }
-
+  # js_library() closure compiler targets, primarily used on ChromeOS. Only
+  # generate these targets if the mojom target is not C++ only and is not using
+  # TypeScript.
+  if (generate_mojom_closure_libraries &&
+      (!defined(invoker.cpp_only) || !invoker.cpp_only) && generate_legacy_js) {
     js_library_for_compile_target_name = "${target_name}_js_library_for_compile"
     if (sources_list != []) {
       js_library(js_library_for_compile_target_name) {
@@ -1834,35 +2039,9 @@ template("mojom") {
       }
     }
 
-    js_modules_target_name = "${target_name}_js_modules"
-    if (sources_list != []) {
-      js_library(js_modules_target_name) {
-        extra_public_deps = [ ":$generator_js_target_name" ]
-        sources = []
-        foreach(base_path, output_file_base_paths) {
-          sources += [ "$root_gen_dir/${base_path}.m.js" ]
-        }
-        externs_list = [
-          "${externs_path}/mojo_core.js",
-          "${externs_path}/pending.js",
-        ]
-        if (defined(invoker.disallow_native_types) &&
-            invoker.disallow_native_types) {
-          deps = []
-        } else {
-          deps = [ "//mojo/public/js:bindings_uncompiled" ]
-        }
-        foreach(d, all_deps) {
-          full_name = get_label_info(d, "label_no_toolchain")
-          deps += [ "${full_name}_js_modules" ]
-        }
-      }
-    } else {
-      group(js_modules_target_name) {
-      }
-    }
-
-    if (defined(invoker.webui_module_path)) {
+    # WebUI specific closure targets, not needed by targets that are generating
+    # TypeScript WebUI bindings or by legacy-only targets.
+    if (defined(invoker.webui_module_path) && !use_typescript_for_target) {
       webui_js_target_name = "${target_name}_webui_js"
       if (sources_list != []) {
         js_library(webui_js_target_name) {
@@ -1890,46 +2069,38 @@ template("mojom") {
         group(webui_js_target_name) {
         }
       }
-    }
-  }
-  if ((generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) &&
-      use_typescript_for_target) {
-    generator_js_target_names = []
-    source_filelist = []
-    foreach(source, sources_list) {
-      source_filelist += [ rebase_path("$source", root_build_dir) ]
-    }
 
-    dependency_types = [
-      {
-        name = "regular"
-        ts_extension = ".ts"
-        js_extension = ".js"
-      },
-      {
-        name = "es_modules"
-        ts_extension = ".m.ts"
-        js_extension = ".m.js"
-      },
-    ]
+      webui_grdp_target_name = "${target_name}_webui_grdp"
+      out_grd = "$target_gen_dir/${target_name}_webui_resources.grdp"
+      grd_prefix = "${target_name}_webui"
+      generate_grd(webui_grdp_target_name) {
+        grd_prefix = grd_prefix
+        out_grd = out_grd
 
-    foreach(dependency_type, dependency_types) {
-      ts_outputs = []
-      js_outputs = []
+        deps = [ ":$webui_js_target_name" ]
 
-      foreach(base_path, output_file_base_paths) {
-        ts_outputs +=
-            [ "$root_gen_dir/$base_path-lite${dependency_type.ts_extension}" ]
-        js_outputs +=
-            [ "$root_gen_dir/$base_path-lite${dependency_type.js_extension}" ]
+        input_files = []
+        foreach(base_path, output_file_base_paths) {
+          input_files += [ "${base_path}-webui.js" ]
+        }
+
+        input_files_base_dir =
+            rebase_path("$root_gen_dir/mojom-webui", "$root_build_dir")
+      }
+    }
+  }
+  if ((generate_js_fuzzing || !defined(invoker.cpp_only) ||
+       !invoker.cpp_only) && use_typescript_for_target) {
+    if (sources_list != []) {
+      source_filelist = []
+      foreach(source, sources_list) {
+        source_filelist += [ rebase_path(source, root_build_dir) ]
       }
 
       # Generate Typescript bindings.
-      generator_ts_target_name =
-          "${target_name}_${dependency_type.name}__ts__generator"
+      generator_ts_target_name = "${target_name}_ts__generator"
 
-      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-      python2_action(generator_ts_target_name) {
+      action(generator_ts_target_name) {
         script = mojom_generator_script
         inputs = mojom_generator_sources + jinja2_sources
         sources = sources_list
@@ -1938,7 +2109,10 @@ template("mojom") {
           "//mojo/public/tools/bindings:precompile_templates",
         ]
 
-        outputs = ts_outputs
+        outputs = []
+        foreach(base_path, output_file_base_paths) {
+          outputs += [ "$root_gen_dir/$base_path-webui.ts" ]
+        }
         args = common_generator_args
         response_file_contents = source_filelist
 
@@ -1948,98 +2122,16 @@ template("mojom") {
           "typescript",
         ]
 
-        if (dependency_type.name == "es_modules") {
-          args += [ "--ts_use_es_modules" ]
+        if (!defined(invoker.scramble_message_ids) ||
+            invoker.scramble_message_ids) {
+          inputs += message_scrambling_inputs
+          args += message_scrambling_args
         }
 
-        # TODO(crbug.com/1007587): Support scramble_message_ids.
+        # TODO(crbug.com/1007587): Support scramble_message_ids if above is
+        # insufficient.
         # TODO(crbug.com/1007591): Support generate_fuzzing.
       }
-
-      # Create tsconfig.json for the generated Typescript.
-      tsconfig_filename =
-          "$target_gen_dir/$target_name-${dependency_type.name}-tsconfig.json"
-      tsconfig = {
-      }
-      tsconfig.compilerOptions = {
-        composite = true
-        target = "es6"
-        module = "es6"
-        lib = [
-          "es6",
-          "esnext.bigint",
-        ]
-        strict = true
-      }
-      tsconfig.files = []
-      foreach(base_path, output_file_base_paths) {
-        tsconfig.files += [ rebase_path(
-                "$root_gen_dir/$base_path-lite${dependency_type.ts_extension}",
-                target_gen_dir,
-                root_gen_dir) ]
-      }
-      tsconfig.references = []
-
-      # Get tsconfigs for deps.
-      foreach(d, all_deps) {
-        dep_target_gen_dir = rebase_path(get_label_info(d, "target_gen_dir"))
-        dep_name = get_label_info(d, "name")
-        reference = {
-        }
-        reference.path = "$dep_target_gen_dir/$dep_name-${dependency_type.name}-tsconfig.json"
-        tsconfig.references += [ reference ]
-      }
-      write_file(tsconfig_filename, tsconfig, "json")
-
-      # Compile previously generated Typescript to Javascript.
-      generator_js_target_name =
-          "${target_name}_${dependency_type.name}__js__generator"
-      generator_js_target_names += [ generator_js_target_name ]
-
-      # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.
-      python2_action(generator_js_target_name) {
-        script = "$mojom_generator_root/compile_typescript.py"
-        sources = ts_outputs
-        outputs = js_outputs
-        public_deps = [ ":$generator_ts_target_name" ]
-        foreach(d, all_deps) {
-          full_name = get_label_info(d, "label_no_toolchain")
-          public_deps +=
-              [ "${full_name}_${dependency_type.name}__js__generator" ]
-        }
-
-        absolute_tsconfig_path =
-            rebase_path(tsconfig_filename, "", target_gen_dir)
-        args = [ "--tsconfig_path=$absolute_tsconfig_path" ]
-      }
-    }
-
-    js_target_name = target_name + "_js"
-    group(js_target_name) {
-      public_deps = []
-      if (sources_list != []) {
-        foreach(generator_js_target_name, generator_js_target_names) {
-          public_deps += [ ":$generator_js_target_name" ]
-        }
-      }
-
-      foreach(d, all_deps) {
-        full_name = get_label_info(d, "label_no_toolchain")
-        public_deps += [ "${full_name}_js" ]
-      }
-    }
-
-    group(js_data_deps_target_name) {
-      data = js_outputs
-      deps = []
-      foreach(generator_js_target_name, generator_js_target_names) {
-        deps += [ ":$generator_js_target_name" ]
-      }
-      data_deps = []
-      foreach(d, all_deps) {
-        full_name = get_label_info(d, "label_no_toolchain")
-        data_deps += [ "${full_name}_js_data_deps" ]
-      }
     }
   }
 }
@@ -2063,3 +2155,53 @@ template("mojom_component") {
     component_macro_prefix = invoker.macro_prefix
   }
 }
+
+# A helper for the mojom() template above for defining a WebUIJsBridge.
+# Supports all the same arguments as mojom() except for `sources` and adds
+# `source`, `webui_controller`, `webui_controller_header`, and
+# `webui_controller_deps` which are *required*. See below for more details.
+#
+# A WebUIJsBridge is a Mojo interface that contains binders for all interfaces
+# that can be bound from a WebUI's JavaScript. This taret
+# will generate a C++ implementation of the WebUIJsBridge interface
+# and a {target_name}_webui_js_bridge_impl target that can be depended
+# on.
+#
+# The following arguments are required:
+#  - `source`: The mojom file that defines the WebUIJsBridge interface. It's
+#    allowed to define other interfaces.
+#  - `webui_controller`: The name of the WebUIController associated with the
+#    WebUIJsBridge.
+#  - `webui_controller_header`: The header where the WebUIController
+#    is declared.
+#  - `webui_controller_deps`: The target containing the WebUIController.
+template("mojom_with_webui_js_bridge") {
+  assert(defined(invoker.source))
+  assert(defined(invoker.webui_controller),
+         "mojom_with_webui_js_bridge should have a " +
+             "`webui_controller` parameter e.g. `foo::FooUI`.")
+  assert(defined(invoker.webui_controller_header),
+         "mojom_with_webui_js_bridge should have a " +
+             "`webui_controller_header` parameter.")
+  assert(defined(invoker.webui_controller_deps),
+         "mojom_with_webui_js_bridge should have a " +
+             "`webui_controller_deps` parameter.")
+
+  mojom(target_name) {
+    forward_variables_from(invoker,
+                           "*",
+                           [
+                             "source",
+                             "webui_controller",
+                             "webui_controller_header",
+                             "webui_controller_deps",
+                           ])
+    sources = [ invoker.source ]
+
+    webui_js_bridge_config = {
+      webui_controller = invoker.webui_controller
+      webui_controller_header = invoker.webui_controller_header
+      webui_controller_deps = invoker.webui_controller_deps
+    }
+  }
+}
diff --git a/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator.py b/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator.py
index da9efc71..98cac149 100755
--- a/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator.py
+++ b/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -51,16 +51,23 @@ import crbug_1001171
 
 _BUILTIN_GENERATORS = {
     "c++": "mojom_cpp_generator",
+    "webui_js_bridge": "mojom_webui_js_bridge_generator",
     "javascript": "mojom_js_generator",
     "java": "mojom_java_generator",
     "mojolpm": "mojom_mojolpm_generator",
     "typescript": "mojom_ts_generator",
 }
 
+_BUILTIN_CHECKS = {
+    "attributes": "mojom_attributes_check",
+    "definitions": "mojom_definitions_check",
+    "restrictions": "mojom_restrictions_check",
+}
+
 
 def LoadGenerators(generators_string):
   if not generators_string:
-    return []  # No generators.
+    return {}  # No generators.
 
   generators = {}
   for generator_name in [s.strip() for s in generators_string.split(",")]:
@@ -74,6 +81,21 @@ def LoadGenerators(generators_string):
   return generators
 
 
+def LoadChecks(checks_string):
+  if not checks_string:
+    return {}  # No checks.
+
+  checks = {}
+  for check_name in [s.strip() for s in checks_string.split(",")]:
+    check = check_name.lower()
+    if check not in _BUILTIN_CHECKS:
+      print("Unknown check name %s" % check_name)
+      sys.exit(1)
+    check_module = importlib.import_module("checks.%s" % _BUILTIN_CHECKS[check])
+    checks[check] = check_module
+  return checks
+
+
 def MakeImportStackMessage(imported_filename_stack):
   """Make a (human-readable) message listing a chain of imports. (Returned
   string begins with a newline (if nonempty) and does not end with one.)"""
@@ -82,7 +104,7 @@ def MakeImportStackMessage(imported_filename_stack):
                     zip(imported_filename_stack[1:], imported_filename_stack)]))
 
 
-class RelativePath(object):
+class RelativePath:
   """Represents a path relative to the source tree or generated output dir."""
 
   def __init__(self, path, source_root, output_dir):
@@ -142,7 +164,7 @@ def ReadFileContents(filename):
     return f.read()
 
 
-class MojomProcessor(object):
+class MojomProcessor:
   """Takes parsed mojom modules and generates language bindings from them.
 
   Attributes:
@@ -169,8 +191,8 @@ class MojomProcessor(object):
     if 'c++' in self._typemap:
       self._typemap['mojolpm'] = self._typemap['c++']
 
-  def _GenerateModule(self, args, remaining_args, generator_modules,
-                      rel_filename, imported_filename_stack):
+  def _GenerateModule(self, args, remaining_args, check_modules,
+                      generator_modules, rel_filename, imported_filename_stack):
     # Return the already-generated module.
     if rel_filename.path in self._processed_files:
       return self._processed_files[rel_filename.path]
@@ -190,12 +212,16 @@ class MojomProcessor(object):
       ScrambleMethodOrdinals(module.interfaces, salt)
 
     if self._should_generate(rel_filename.path):
+      # Run checks on module first.
+      for check_module in check_modules.values():
+        checker = check_module.Check(module)
+        checker.CheckModule()
+      # Then run generation.
       for language, generator_module in generator_modules.items():
         generator = generator_module.Generator(
             module, args.output_dir, typemap=self._typemap.get(language, {}),
             variant=args.variant, bytecode_path=args.bytecode_path,
             for_blink=args.for_blink,
-            js_bindings_mode=args.js_bindings_mode,
             js_generate_struct_deserializers=\
                     args.js_generate_struct_deserializers,
             export_attribute=args.export_attribute,
@@ -234,6 +260,7 @@ def _Generate(args, remaining_args):
       args.import_directories[idx] = RelativePath(tokens[0], args.depth,
                                                   args.output_dir)
   generator_modules = LoadGenerators(args.generators_string)
+  check_modules = LoadChecks(args.checks_string)
 
   fileutil.EnsureDirectoryExists(args.output_dir)
 
@@ -246,7 +273,7 @@ def _Generate(args, remaining_args):
 
   for filename in args.filename:
     processor._GenerateModule(
-        args, remaining_args, generator_modules,
+        args, remaining_args, check_modules, generator_modules,
         RelativePath(filename, args.depth, args.output_dir), [])
 
   return 0
@@ -286,6 +313,12 @@ def main():
                                metavar="GENERATORS",
                                default="c++,javascript,java,mojolpm",
                                help="comma-separated list of generators")
+  generate_parser.add_argument("-c",
+                               "--checks",
+                               dest="checks_string",
+                               metavar="CHECKS",
+                               default="attributes,definitions,restrictions",
+                               help="comma-separated list of checks")
   generate_parser.add_argument(
       "--gen_dir", dest="gen_directories", action="append", metavar="directory",
       default=[], help="add a directory to be searched for the syntax trees.")
@@ -308,11 +341,6 @@ def main():
   generate_parser.add_argument("--for_blink", action="store_true",
                                help="Use WTF types as generated types for mojo "
                                "string/array/map.")
-  generate_parser.add_argument(
-      "--js_bindings_mode", choices=["new", "old"], default="old",
-      help="This option only affects the JavaScript bindings. The value could "
-      "be \"new\" to generate new-style lite JS bindings in addition to the "
-      "old, or \"old\" to only generate old bindings.")
   generate_parser.add_argument(
       "--js_generate_struct_deserializers", action="store_true",
       help="Generate javascript deserialize methods for structs in "
@@ -387,4 +415,10 @@ def main():
 
 if __name__ == "__main__":
   with crbug_1001171.DumpStateOnLookupError():
-    sys.exit(main())
+    ret = main()
+    # Exit without running GC, which can save multiple seconds due to the large
+    # number of object created. But flush is necessary as os._exit doesn't do
+    # that.
+    sys.stdout.flush()
+    sys.stderr.flush()
+    os._exit(ret)
diff --git a/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py b/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
index bddbe3f4..761922b6 100644
--- a/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
+++ b/utils/ipc/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -8,13 +8,13 @@ from mojom_bindings_generator import MakeImportStackMessage
 from mojom_bindings_generator import ScrambleMethodOrdinals
 
 
-class FakeIface(object):
+class FakeIface:
   def __init__(self):
     self.mojom_name = None
     self.methods = None
 
 
-class FakeMethod(object):
+class FakeMethod:
   def __init__(self, explicit_ordinal=None):
     self.explicit_ordinal = explicit_ordinal
     self.ordinal = explicit_ordinal
diff --git a/utils/ipc/mojo/public/tools/bindings/validate_typemap_config.py b/utils/ipc/mojo/public/tools/bindings/validate_typemap_config.py
index f1783d59..1d940a21 100755
--- a/utils/ipc/mojo/public/tools/bindings/validate_typemap_config.py
+++ b/utils/ipc/mojo/public/tools/bindings/validate_typemap_config.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -17,7 +17,7 @@ def CheckCppTypemapConfigs(target_name, config_filename, out_filename):
   ])
   _SUPPORTED_TYPE_KEYS = set([
       'mojom', 'cpp', 'copyable_pass_by_value', 'force_serialize', 'hashable',
-      'move_only', 'nullable_is_same_type'
+      'move_only', 'nullable_is_same_type', 'forward_declaration'
   ])
   with open(config_filename, 'r') as f:
     for config in json.load(f):
diff --git a/utils/ipc/mojo/public/tools/mojom/BUILD.gn b/utils/ipc/mojo/public/tools/mojom/BUILD.gn
new file mode 100644
index 00000000..4e456c0e
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/mojom/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("tests") {
+  data = [
+    "check_stable_mojom_compatibility_unittest.py",
+    "check_stable_mojom_compatibility.py",
+    "const_unittest.py",
+    "enum_unittest.py",
+    "mojom_parser_test_case.py",
+    "mojom_parser_unittest.py",
+    "mojom_parser.py",
+    "stable_attribute_unittest.py",
+    "version_compatibility_unittest.py",
+  ]
+}
diff --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
index 08bd672f..3c1ac42c 100755
--- a/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py
+++ b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2020 The Chromium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Verifies backward-compatibility of mojom type changes.
@@ -12,20 +12,18 @@ This can be used e.g. by a presubmit check to prevent developers from making
 breaking changes to stable mojoms."""
 
 import argparse
-import errno
 import io
 import json
 import os
 import os.path
-import shutil
-import six
 import sys
-import tempfile
 
 from mojom.generate import module
 from mojom.generate import translate
 from mojom.parse import parser
 
+# pylint: disable=raise-missing-from
+
 
 class ParseError(Exception):
   pass
@@ -41,6 +39,8 @@ def _ValidateDelta(root, delta):
   transitive closure of a mojom's input dependencies all at once.
   """
 
+  translate.is_running_backwards_compatibility_check_hack = True
+
   # First build a map of all files covered by the delta
   affected_files = set()
   old_files = {}
@@ -73,11 +73,33 @@ def _ValidateDelta(root, delta):
     try:
       ast = parser.Parse(contents, mojom)
     except Exception as e:
-      six.reraise(
-          ParseError,
-          'encountered exception {0} while parsing {1}'.format(e, mojom),
-          sys.exc_info()[2])
+      raise ParseError('encountered exception {0} while parsing {1}'.format(
+          e, mojom))
+
+    # Files which are generated at compile time can't be checked by this script
+    # (at the moment) since they may not exist in the output directory.
+    generated_files_to_skip = {
+        ('third_party/blink/public/mojom/runtime_feature_state/'
+         'runtime_feature_state.mojom'),
+    }
+
+    ast.import_list.items = [
+        x for x in ast.import_list.items
+        if x.import_filename not in generated_files_to_skip
+    ]
+
     for imp in ast.import_list:
+      if (not file_overrides.get(imp.import_filename)
+          and not os.path.exists(os.path.join(root, imp.import_filename))):
+        # Speculatively construct a path prefix to locate the import_filename
+        mojom_path = os.path.dirname(os.path.normpath(mojom)).split(os.sep)
+        test_prefix = ''
+        for path_component in mojom_path:
+          test_prefix = os.path.join(test_prefix, path_component)
+          test_import_filename = os.path.join(test_prefix, imp.import_filename)
+          if os.path.exists(os.path.join(root, test_import_filename)):
+            imp.import_filename = test_import_filename
+            break
       parseMojom(imp.import_filename, file_overrides, override_modules)
 
     # Now that the transitive set of dependencies has been imported and parsed
@@ -89,10 +111,10 @@ def _ValidateDelta(root, delta):
     modules[mojom] = translate.OrderedModule(ast, mojom, all_modules)
 
   old_modules = {}
-  for mojom in old_files.keys():
+  for mojom in old_files:
     parseMojom(mojom, old_files, old_modules)
   new_modules = {}
-  for mojom in new_files.keys():
+  for mojom in new_files:
     parseMojom(mojom, new_files, new_modules)
 
   # At this point we have a complete set of translated Modules from both the
diff --git a/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility_unittest.py b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility_unittest.py
index 9f51ea77..06769c95 100755
--- a/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility_unittest.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2020 The Chromium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -15,7 +15,7 @@ import check_stable_mojom_compatibility
 from mojom.generate import module
 
 
-class Change(object):
+class Change:
   """Helper to clearly define a mojom file delta to be analyzed."""
 
   def __init__(self, filename, old=None, new=None):
@@ -28,7 +28,7 @@ class Change(object):
 
 class UnchangedFile(Change):
   def __init__(self, filename, contents):
-    super(UnchangedFile, self).__init__(filename, old=contents, new=contents)
+    super().__init__(filename, old=contents, new=contents)
 
 
 class CheckStableMojomCompatibilityTest(unittest.TestCase):
@@ -258,3 +258,82 @@ class CheckStableMojomCompatibilityTest(unittest.TestCase):
                [Stable] struct T { foo.S s; int32 x; };
                """)
     ])
+
+  def testWithPartialImport(self):
+    """The compatibility checking tool correctly parses imports with partial
+    paths."""
+    self.assertBackwardCompatible([
+        UnchangedFile('foo/foo.mojom', 'module foo; [Stable] struct S {};'),
+        Change('foo/bar.mojom',
+               old="""\
+               module bar;
+               import "foo/foo.mojom";
+               [Stable] struct T { foo.S s; };
+               """,
+               new="""\
+               module bar;
+               import "foo.mojom";
+               [Stable] struct T { foo.S s; };
+               """)
+    ])
+
+    self.assertBackwardCompatible([
+        UnchangedFile('foo/foo.mojom', 'module foo; [Stable] struct S {};'),
+        Change('foo/bar.mojom',
+               old="""\
+               module bar;
+               import "foo.mojom";
+               [Stable] struct T { foo.S s; };
+               """,
+               new="""\
+               module bar;
+               import "foo/foo.mojom";
+               [Stable] struct T { foo.S s; };
+               """)
+    ])
+
+    self.assertNotBackwardCompatible([
+        UnchangedFile('foo/foo.mojom', 'module foo; [Stable] struct S {};'),
+        Change('bar/bar.mojom',
+               old="""\
+               module bar;
+               import "foo/foo.mojom";
+               [Stable] struct T { foo.S s; };
+               """,
+               new="""\
+               module bar;
+               import "foo.mojom";
+               [Stable] struct T { foo.S s; };
+               """)
+    ])
+
+    self.assertNotBackwardCompatible([
+        UnchangedFile('foo/foo.mojom', 'module foo; [Stable] struct S {};'),
+        Change('bar/bar.mojom',
+               old="""\
+               module bar;
+               import "foo.mojom";
+               [Stable] struct T { foo.S s; };
+               """,
+               new="""\
+               module bar;
+               import "foo/foo.mojom";
+               [Stable] struct T { foo.S s; };
+               """)
+    ])
+
+  def testNewEnumDefault(self):
+    # Should be backwards compatible since it does not affect the wire format.
+    # This specific case also checks that the backwards compatibility checker
+    # does not throw an error due to the older version of the enum not
+    # specifying [Default].
+    self.assertBackwardCompatible([
+        Change('foo/foo.mojom',
+               old='[Extensible] enum E { One };',
+               new='[Extensible] enum E { [Default] One };')
+    ])
+    self.assertBackwardCompatible([
+        Change('foo/foo.mojom',
+               old='[Extensible] enum E { [Default] One, Two, };',
+               new='[Extensible] enum E { One, [Default] Two, };')
+    ])
diff --git a/utils/ipc/mojo/public/tools/mojom/const_unittest.py b/utils/ipc/mojo/public/tools/mojom/const_unittest.py
index cb42dfac..e8ed36a7 100644
--- a/utils/ipc/mojo/public/tools/mojom/const_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/const_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/utils/ipc/mojo/public/tools/mojom/enum_unittest.py b/utils/ipc/mojo/public/tools/mojom/enum_unittest.py
index d9005078..9269cde5 100644
--- a/utils/ipc/mojo/public/tools/mojom/enum_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/enum_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -90,3 +90,31 @@ class EnumTest(MojomParserTestCase):
     self.assertEqual('F', b.enums[0].mojom_name)
     self.assertEqual('kFoo', b.enums[0].fields[0].mojom_name)
     self.assertEqual(37, b.enums[0].fields[0].numeric_value)
+
+  def testEnumAttributesAreEnums(self):
+    """Verifies that enum values in attributes are really enum types."""
+    a_mojom = 'a.mojom'
+    self.WriteFile(a_mojom, 'module a; enum E { kFoo, kBar };')
+    b_mojom = 'b.mojom'
+    self.WriteFile(
+        b_mojom, 'module b;'
+        'import "a.mojom";'
+        '[MooCow=a.E.kFoo]'
+        'interface Foo { Foo(); };')
+    self.ParseMojoms([a_mojom, b_mojom])
+    b = self.LoadModule(b_mojom)
+    self.assertEqual(b.interfaces[0].attributes['MooCow'].mojom_name, 'kFoo')
+
+  def testConstantAttributes(self):
+    """Verifies that constants as attributes are translated to the constant."""
+    a_mojom = 'a.mojom'
+    self.WriteFile(
+        a_mojom, 'module a;'
+        'enum E { kFoo, kBar };'
+        'const E kB = E.kFoo;'
+        '[Attr=kB] interface Hello { Foo(); };')
+    self.ParseMojoms([a_mojom])
+    a = self.LoadModule(a_mojom)
+    self.assertEqual(a.interfaces[0].attributes['Attr'].mojom_name, 'kB')
+    self.assertEquals(a.interfaces[0].attributes['Attr'].value.mojom_name,
+                      'kFoo')
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn
index 51facc0c..a0edf0eb 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -8,6 +8,7 @@ group("mojom") {
     "error.py",
     "fileutil.py",
     "generate/__init__.py",
+    "generate/check.py",
     "generate/generator.py",
     "generate/module.py",
     "generate/pack.py",
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/error.py b/utils/ipc/mojo/public/tools/mojom/mojom/error.py
index 8a1e03da..dd53b835 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/error.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/error.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py
index bf626f54..29daec36 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil.py
@@ -1,4 +1,4 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
+# Copyright 2015 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py
index ff5753a2..48eaf4ec 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/fileutil_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
+# Copyright 2015 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/check.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/check.py
new file mode 100644
index 00000000..1efe2022
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/check.py
@@ -0,0 +1,26 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Code shared by the various pre-generation mojom checkers."""
+
+
+class CheckException(Exception):
+  def __init__(self, module, message):
+    self.module = module
+    self.message = message
+    super().__init__(self.message)
+
+  def __str__(self):
+    return "Failed mojo pre-generation check for {}:\n{}".format(
+        self.module.path, self.message)
+
+
+class Check:
+  def __init__(self, module):
+    self.module = module
+
+  def CheckModule(self):
+    """ Subclass should return True if its Checks pass, and throw an
+    exception otherwise. CheckModule will be called immediately before
+    mojom.generate.Generator.GenerateFiles()"""
+    raise NotImplementedError("Subclasses must override/implement this method")
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py
index 4a1c73fc..31cacd0e 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py
@@ -1,4 +1,4 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Code shared by the various language-specific code generators."""
@@ -97,7 +97,7 @@ def ToLowerSnakeCase(identifier):
   return _ToSnakeCase(identifier, upper=False)
 
 
-class Stylizer(object):
+class Stylizer:
   """Stylizers specify naming rules to map mojom names to names in generated
   code. For example, if you would like method_name in mojom to be mapped to
   MethodName in the generated code, you need to define a subclass of Stylizer
@@ -233,7 +233,7 @@ def AddComputedData(module):
     _AddInterfaceComputedData(interface)
 
 
-class Generator(object):
+class Generator:
   # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
   # files to stdout.
   def __init__(self,
@@ -243,7 +243,6 @@ class Generator(object):
                variant=None,
                bytecode_path=None,
                for_blink=False,
-               js_bindings_mode="new",
                js_generate_struct_deserializers=False,
                export_attribute=None,
                export_header=None,
@@ -262,7 +261,6 @@ class Generator(object):
     self.variant = variant
     self.bytecode_path = bytecode_path
     self.for_blink = for_blink
-    self.js_bindings_mode = js_bindings_mode
     self.js_generate_struct_deserializers = js_generate_struct_deserializers
     self.export_attribute = export_attribute
     self.export_header = export_header
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
index 32c884a8..76cda398 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py
index 9bdb28e0..f0664b31 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py
@@ -1,4 +1,4 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -12,15 +12,14 @@
 # method = interface.AddMethod('Tat', 0)
 # method.AddParameter('baz', 0, mojom.INT32)
 
-import sys
-if sys.version_info.major == 2:
-  import cPickle as pickle
-else:
-  import pickle
+import pickle
+from collections import OrderedDict
 from uuid import UUID
 
+# pylint: disable=raise-missing-from
 
-class BackwardCompatibilityChecker(object):
+
+class BackwardCompatibilityChecker:
   """Used for memoization while recursively checking two type definitions for
   backward-compatibility."""
 
@@ -64,23 +63,20 @@ def Repr(obj, as_ref=True):
     return obj.Repr(as_ref=as_ref)
   # Since we cannot implement Repr for existing container types, we
   # handle them here.
-  elif isinstance(obj, list):
+  if isinstance(obj, list):
     if not obj:
       return '[]'
-    else:
-      return ('[\n%s\n]' % (',\n'.join(
-          '    %s' % Repr(elem, as_ref).replace('\n', '\n    ')
-          for elem in obj)))
-  elif isinstance(obj, dict):
+    return ('[\n%s\n]' %
+            (',\n'.join('    %s' % Repr(elem, as_ref).replace('\n', '\n    ')
+                        for elem in obj)))
+  if isinstance(obj, dict):
     if not obj:
       return '{}'
-    else:
-      return ('{\n%s\n}' % (',\n'.join(
-          '    %s: %s' % (Repr(key, as_ref).replace('\n', '\n    '),
-                          Repr(val, as_ref).replace('\n', '\n    '))
-          for key, val in obj.items())))
-  else:
-    return repr(obj)
+    return ('{\n%s\n}' % (',\n'.join('    %s: %s' %
+                                     (Repr(key, as_ref).replace('\n', '\n    '),
+                                      Repr(val, as_ref).replace('\n', '\n    '))
+                                     for key, val in obj.items())))
+  return repr(obj)
 
 
 def GenericRepr(obj, names):
@@ -104,7 +100,7 @@ def GenericRepr(obj, names):
       ReprIndent(name, as_ref) for (name, as_ref) in names.items()))
 
 
-class Kind(object):
+class Kind:
   """Kind represents a type (e.g. int8, string).
 
   Attributes:
@@ -112,16 +108,43 @@ class Kind(object):
     module: {Module} The defining module. Set to None for built-in types.
     parent_kind: The enclosing type. For example, an enum defined
         inside an interface has that interface as its parent. May be None.
+    is_nullable: True if the type is nullable.
   """
 
-  def __init__(self, spec=None, module=None):
+  def __init__(self, spec=None, is_nullable=False, module=None):
     self.spec = spec
     self.module = module
     self.parent_kind = None
+    self.is_nullable = is_nullable
+    self.shared_definition = {}
+
+  @classmethod
+  def AddSharedProperty(cls, name):
+    """Adds a property |name| to |cls|, which accesses the corresponding item in
+       |shared_definition|.
+
+       The reason of adding such indirection is to enable sharing definition
+       between a reference kind and its nullable variation. For example:
+         a = Struct('test_struct_1')
+         b = a.MakeNullableKind()
+         a.name = 'test_struct_2'
+         print(b.name)  # Outputs 'test_struct_2'.
+    """
+    def Get(self):
+      try:
+        return self.shared_definition[name]
+      except KeyError:  # Must raise AttributeError if property doesn't exist.
+        raise AttributeError
+
+    def Set(self, value):
+      self.shared_definition[name] = value
+
+    setattr(cls, name, property(Get, Set))
 
   def Repr(self, as_ref=True):
     # pylint: disable=unused-argument
-    return '<%s spec=%r>' % (self.__class__.__name__, self.spec)
+    return '<%s spec=%r is_nullable=%r>' % (self.__class__.__name__, self.spec,
+                                            self.is_nullable)
 
   def __repr__(self):
     # Gives us a decent __repr__ for all kinds.
@@ -130,7 +153,8 @@ class Kind(object):
   def __eq__(self, rhs):
     # pylint: disable=unidiomatic-typecheck
     return (type(self) == type(rhs)
-            and (self.spec, self.parent_kind) == (rhs.spec, rhs.parent_kind))
+            and (self.spec, self.parent_kind, self.is_nullable)
+            == (rhs.spec, rhs.parent_kind, rhs.is_nullable))
 
   def __hash__(self):
     # TODO(crbug.com/1060471): Remove this and other __hash__ methods on Kind
@@ -138,32 +162,113 @@ class Kind(object):
     # some primitive Kinds as dict keys. The default hash (object identity)
     # breaks these dicts when a pickled Module instance is unpickled and used
     # during a subsequent run of the parser.
-    return hash((self.spec, self.parent_kind))
+    return hash((self.spec, self.parent_kind, self.is_nullable))
 
   # pylint: disable=unused-argument
   def IsBackwardCompatible(self, rhs, checker):
     return self == rhs
 
 
+class ValueKind(Kind):
+  """ValueKind represents values that aren't reference kinds.
+
+  The primary difference is the wire representation for nullable value kinds
+  still reserves space for the value type itself, even if that value itself
+  is logically null.
+  """
+  def __init__(self, spec=None, is_nullable=False, module=None):
+    assert spec is None or is_nullable == spec.startswith('?')
+    Kind.__init__(self, spec, is_nullable, module)
+
+  def MakeNullableKind(self):
+    assert not self.is_nullable
+
+    if self == BOOL:
+      return NULLABLE_BOOL
+    if self == INT8:
+      return NULLABLE_INT8
+    if self == INT16:
+      return NULLABLE_INT16
+    if self == INT32:
+      return NULLABLE_INT32
+    if self == INT64:
+      return NULLABLE_INT64
+    if self == UINT8:
+      return NULLABLE_UINT8
+    if self == UINT16:
+      return NULLABLE_UINT16
+    if self == UINT32:
+      return NULLABLE_UINT32
+    if self == UINT64:
+      return NULLABLE_UINT64
+    if self == FLOAT:
+      return NULLABLE_FLOAT
+    if self == DOUBLE:
+      return NULLABLE_DOUBLE
+
+    nullable_kind = type(self)()
+    nullable_kind.shared_definition = self.shared_definition
+    if self.spec is not None:
+      nullable_kind.spec = '?' + self.spec
+    nullable_kind.is_nullable = True
+    nullable_kind.parent_kind = self.parent_kind
+    nullable_kind.module = self.module
+
+    return nullable_kind
+
+  def MakeUnnullableKind(self):
+    assert self.is_nullable
+
+    if self == NULLABLE_BOOL:
+      return BOOL
+    if self == NULLABLE_INT8:
+      return INT8
+    if self == NULLABLE_INT16:
+      return INT16
+    if self == NULLABLE_INT32:
+      return INT32
+    if self == NULLABLE_INT64:
+      return INT64
+    if self == NULLABLE_UINT8:
+      return UINT8
+    if self == NULLABLE_UINT16:
+      return UINT16
+    if self == NULLABLE_UINT32:
+      return UINT32
+    if self == NULLABLE_UINT64:
+      return UINT64
+    if self == NULLABLE_FLOAT:
+      return FLOAT
+    if self == NULLABLE_DOUBLE:
+      return DOUBLE
+
+    nullable_kind = type(self)()
+    nullable_kind.shared_definition = self.shared_definition
+    if self.spec is not None:
+      nullable_kind.spec = self.spec[1:]
+    nullable_kind.is_nullable = False
+    nullable_kind.parent_kind = self.parent_kind
+    nullable_kind.module = self.module
+
+    return nullable_kind
+
+  def __eq__(self, rhs):
+    return (isinstance(rhs, ValueKind) and super().__eq__(rhs))
+
+  def __hash__(self):  # pylint: disable=useless-super-delegation
+    return super().__hash__()
+
+
 class ReferenceKind(Kind):
   """ReferenceKind represents pointer and handle types.
 
   A type is nullable if null (for pointer types) or invalid handle (for handle
   types) is a legal value for the type.
-
-  Attributes:
-    is_nullable: True if the type is nullable.
   """
 
   def __init__(self, spec=None, is_nullable=False, module=None):
     assert spec is None or is_nullable == spec.startswith('?')
-    Kind.__init__(self, spec, module)
-    self.is_nullable = is_nullable
-    self.shared_definition = {}
-
-  def Repr(self, as_ref=True):
-    return '<%s spec=%r is_nullable=%r>' % (self.__class__.__name__, self.spec,
-                                            self.is_nullable)
+    Kind.__init__(self, spec, is_nullable, module)
 
   def MakeNullableKind(self):
     assert not self.is_nullable
@@ -193,55 +298,36 @@ class ReferenceKind(Kind):
 
     return nullable_kind
 
-  @classmethod
-  def AddSharedProperty(cls, name):
-    """Adds a property |name| to |cls|, which accesses the corresponding item in
-       |shared_definition|.
-
-       The reason of adding such indirection is to enable sharing definition
-       between a reference kind and its nullable variation. For example:
-         a = Struct('test_struct_1')
-         b = a.MakeNullableKind()
-         a.name = 'test_struct_2'
-         print(b.name)  # Outputs 'test_struct_2'.
-    """
-
-    def Get(self):
-      try:
-        return self.shared_definition[name]
-      except KeyError:  # Must raise AttributeError if property doesn't exist.
-        raise AttributeError
-
-    def Set(self, value):
-      self.shared_definition[name] = value
-
-    setattr(cls, name, property(Get, Set))
-
   def __eq__(self, rhs):
-    return (isinstance(rhs, ReferenceKind)
-            and super(ReferenceKind, self).__eq__(rhs)
-            and self.is_nullable == rhs.is_nullable)
-
-  def __hash__(self):
-    return hash((super(ReferenceKind, self).__hash__(), self.is_nullable))
+    return (isinstance(rhs, ReferenceKind) and super().__eq__(rhs))
 
-  def IsBackwardCompatible(self, rhs, checker):
-    return (super(ReferenceKind, self).IsBackwardCompatible(rhs, checker)
-            and self.is_nullable == rhs.is_nullable)
+  def __hash__(self):  # pylint: disable=useless-super-delegation
+    return super().__hash__()
 
 
 # Initialize the set of primitive types. These can be accessed by clients.
-BOOL = Kind('b')
-INT8 = Kind('i8')
-INT16 = Kind('i16')
-INT32 = Kind('i32')
-INT64 = Kind('i64')
-UINT8 = Kind('u8')
-UINT16 = Kind('u16')
-UINT32 = Kind('u32')
-UINT64 = Kind('u64')
-FLOAT = Kind('f')
-DOUBLE = Kind('d')
+BOOL = ValueKind('b')
+INT8 = ValueKind('i8')
+INT16 = ValueKind('i16')
+INT32 = ValueKind('i32')
+INT64 = ValueKind('i64')
+UINT8 = ValueKind('u8')
+UINT16 = ValueKind('u16')
+UINT32 = ValueKind('u32')
+UINT64 = ValueKind('u64')
+FLOAT = ValueKind('f')
+DOUBLE = ValueKind('d')
+NULLABLE_BOOL = ValueKind('?b', True)
+NULLABLE_INT8 = ValueKind('?i8', True)
+NULLABLE_INT16 = ValueKind('?i16', True)
+NULLABLE_INT32 = ValueKind('?i32', True)
+NULLABLE_INT64 = ValueKind('?i64', True)
+NULLABLE_UINT8 = ValueKind('?u8', True)
+NULLABLE_UINT16 = ValueKind('?u16', True)
+NULLABLE_UINT32 = ValueKind('?u32', True)
+NULLABLE_UINT64 = ValueKind('?u64', True)
+NULLABLE_FLOAT = ValueKind('?f', True)
+NULLABLE_DOUBLE = ValueKind('?d', True)
 STRING = ReferenceKind('s')
 HANDLE = ReferenceKind('h')
 DCPIPE = ReferenceKind('h:d:c')
@@ -270,6 +356,17 @@ PRIMITIVES = (
     UINT64,
     FLOAT,
     DOUBLE,
+    NULLABLE_BOOL,
+    NULLABLE_INT8,
+    NULLABLE_INT16,
+    NULLABLE_INT32,
+    NULLABLE_INT64,
+    NULLABLE_UINT8,
+    NULLABLE_UINT16,
+    NULLABLE_UINT32,
+    NULLABLE_UINT64,
+    NULLABLE_FLOAT,
+    NULLABLE_DOUBLE,
     STRING,
     HANDLE,
     DCPIPE,
@@ -294,9 +391,12 @@ ATTRIBUTE_STABLE = 'Stable'
 ATTRIBUTE_SYNC = 'Sync'
 ATTRIBUTE_UNLIMITED_SIZE = 'UnlimitedSize'
 ATTRIBUTE_UUID = 'Uuid'
+ATTRIBUTE_SERVICE_SANDBOX = 'ServiceSandbox'
+ATTRIBUTE_REQUIRE_CONTEXT = 'RequireContext'
+ATTRIBUTE_ALLOWED_CONTEXT = 'AllowedContext'
 
 
-class NamedValue(object):
+class NamedValue:
   def __init__(self, module, parent_kind, mojom_name):
     self.module = module
     self.parent_kind = parent_kind
@@ -316,7 +416,7 @@ class NamedValue(object):
     return hash((self.parent_kind, self.mojom_name))
 
 
-class BuiltinValue(object):
+class BuiltinValue:
   def __init__(self, value):
     self.value = value
 
@@ -350,7 +450,7 @@ class EnumValue(NamedValue):
     return self.field.name
 
 
-class Constant(object):
+class Constant:
   def __init__(self, mojom_name=None, kind=None, value=None, parent_kind=None):
     self.mojom_name = mojom_name
     self.name = None
@@ -368,7 +468,7 @@ class Constant(object):
                                        rhs.parent_kind))
 
 
-class Field(object):
+class Field:
   def __init__(self,
                mojom_name=None,
                kind=None,
@@ -414,7 +514,18 @@ class StructField(Field):
 
 
 class UnionField(Field):
-  pass
+  def __init__(self,
+               mojom_name=None,
+               kind=None,
+               ordinal=None,
+               default=None,
+               attributes=None):
+    Field.__init__(self, mojom_name, kind, ordinal, default, attributes)
+
+  @property
+  def is_default(self):
+    return self.attributes.get(ATTRIBUTE_DEFAULT, False) \
+        if self.attributes else False
 
 
 def _IsFieldBackwardCompatible(new_field, old_field, checker):
@@ -441,14 +552,14 @@ class Struct(ReferenceKind):
         if it's a native struct.
   """
 
-  ReferenceKind.AddSharedProperty('mojom_name')
-  ReferenceKind.AddSharedProperty('name')
-  ReferenceKind.AddSharedProperty('native_only')
-  ReferenceKind.AddSharedProperty('custom_serializer')
-  ReferenceKind.AddSharedProperty('fields')
-  ReferenceKind.AddSharedProperty('enums')
-  ReferenceKind.AddSharedProperty('constants')
-  ReferenceKind.AddSharedProperty('attributes')
+  Kind.AddSharedProperty('mojom_name')
+  Kind.AddSharedProperty('name')
+  Kind.AddSharedProperty('native_only')
+  Kind.AddSharedProperty('custom_serializer')
+  Kind.AddSharedProperty('fields')
+  Kind.AddSharedProperty('enums')
+  Kind.AddSharedProperty('constants')
+  Kind.AddSharedProperty('attributes')
 
   def __init__(self, mojom_name=None, module=None, attributes=None):
     if mojom_name is not None:
@@ -470,12 +581,11 @@ class Struct(ReferenceKind):
       return '<%s mojom_name=%r module=%s>' % (self.__class__.__name__,
                                                self.mojom_name,
                                                Repr(self.module, as_ref=True))
-    else:
-      return GenericRepr(self, {
-          'mojom_name': False,
-          'fields': False,
-          'module': True
-      })
+    return GenericRepr(self, {
+        'mojom_name': False,
+        'fields': False,
+        'module': True
+    })
 
   def AddField(self,
                mojom_name,
@@ -496,13 +606,13 @@ class Struct(ReferenceKind):
     for constant in self.constants:
       constant.Stylize(stylizer)
 
-  def IsBackwardCompatible(self, older_struct, checker):
-    """This struct is backward-compatible with older_struct if and only if all
-    of the following conditions hold:
+  def IsBackwardCompatible(self, rhs, checker):
+    """This struct is backward-compatible with rhs (older_struct) if and only if
+    all of the following conditions hold:
       - Any newly added field is tagged with a [MinVersion] attribute specifying
         a version number greater than all previously used [MinVersion]
         attributes within the struct.
-      - All fields present in older_struct remain present in the new struct,
+      - All fields present in rhs remain present in the new struct,
         with the same ordinal position, same optional or non-optional status,
         same (or backward-compatible) type and where applicable, the same
         [MinVersion] attribute value.
@@ -521,7 +631,7 @@ class Struct(ReferenceKind):
       return fields_by_ordinal
 
     new_fields = buildOrdinalFieldMap(self)
-    old_fields = buildOrdinalFieldMap(older_struct)
+    old_fields = buildOrdinalFieldMap(rhs)
     if len(new_fields) < len(old_fields):
       # At least one field was removed, which is not OK.
       return False
@@ -574,11 +684,18 @@ class Struct(ReferenceKind):
       prefix = self.module.GetNamespacePrefix()
     return '%s%s' % (prefix, self.mojom_name)
 
+  def _tuple(self):
+    return (self.mojom_name, self.native_only, self.fields, self.constants,
+            self.attributes)
+
   def __eq__(self, rhs):
-    return (isinstance(rhs, Struct) and
-            (self.mojom_name, self.native_only, self.fields, self.constants,
-             self.attributes) == (rhs.mojom_name, rhs.native_only, rhs.fields,
-                                  rhs.constants, rhs.attributes))
+    return isinstance(rhs, Struct) and self._tuple() == rhs._tuple()
+
+  def __lt__(self, rhs):
+    if not isinstance(self, type(rhs)):
+      return str(type(self)) < str(type(rhs))
+
+    return self._tuple() < rhs._tuple()
 
   def __hash__(self):
     return id(self)
@@ -595,10 +712,11 @@ class Union(ReferenceKind):
         which Java class name to use to represent it in the generated
         bindings.
   """
-  ReferenceKind.AddSharedProperty('mojom_name')
-  ReferenceKind.AddSharedProperty('name')
-  ReferenceKind.AddSharedProperty('fields')
-  ReferenceKind.AddSharedProperty('attributes')
+  Kind.AddSharedProperty('mojom_name')
+  Kind.AddSharedProperty('name')
+  Kind.AddSharedProperty('fields')
+  Kind.AddSharedProperty('attributes')
+  Kind.AddSharedProperty('default_field')
 
   def __init__(self, mojom_name=None, module=None, attributes=None):
     if mojom_name is not None:
@@ -610,14 +728,14 @@ class Union(ReferenceKind):
     self.name = None
     self.fields = []
     self.attributes = attributes
+    self.default_field = None
 
   def Repr(self, as_ref=True):
     if as_ref:
       return '<%s spec=%r is_nullable=%r fields=%s>' % (
           self.__class__.__name__, self.spec, self.is_nullable, Repr(
               self.fields))
-    else:
-      return GenericRepr(self, {'fields': True, 'is_nullable': False})
+    return GenericRepr(self, {'fields': True, 'is_nullable': False})
 
   def AddField(self, mojom_name, kind, ordinal=None, attributes=None):
     field = UnionField(mojom_name, kind, ordinal, None, attributes)
@@ -629,13 +747,13 @@ class Union(ReferenceKind):
     for field in self.fields:
       field.Stylize(stylizer)
 
-  def IsBackwardCompatible(self, older_union, checker):
-    """This union is backward-compatible with older_union if and only if all
-    of the following conditions hold:
+  def IsBackwardCompatible(self, rhs, checker):
+    """This union is backward-compatible with rhs (older_union) if and only if
+    all of the following conditions hold:
       - Any newly added field is tagged with a [MinVersion] attribute specifying
         a version number greater than all previously used [MinVersion]
         attributes within the union.
-      - All fields present in older_union remain present in the new union,
+      - All fields present in rhs remain present in the new union,
         with the same ordinal value, same optional or non-optional status,
         same (or backward-compatible) type, and where applicable, the same
         [MinVersion] attribute value.
@@ -651,7 +769,7 @@ class Union(ReferenceKind):
       return fields_by_ordinal
 
     new_fields = buildOrdinalFieldMap(self)
-    old_fields = buildOrdinalFieldMap(older_union)
+    old_fields = buildOrdinalFieldMap(rhs)
     if len(new_fields) < len(old_fields):
       # At least one field was removed, which is not OK.
       return False
@@ -677,6 +795,11 @@ class Union(ReferenceKind):
 
     return True
 
+  @property
+  def extensible(self):
+    return self.attributes.get(ATTRIBUTE_EXTENSIBLE, False) \
+        if self.attributes else False
+
   @property
   def stable(self):
     return self.attributes.get(ATTRIBUTE_STABLE, False) \
@@ -690,10 +813,17 @@ class Union(ReferenceKind):
       prefix = self.module.GetNamespacePrefix()
     return '%s%s' % (prefix, self.mojom_name)
 
+  def _tuple(self):
+    return (self.mojom_name, self.fields, self.attributes)
+
   def __eq__(self, rhs):
-    return (isinstance(rhs, Union) and
-            (self.mojom_name, self.fields,
-             self.attributes) == (rhs.mojom_name, rhs.fields, rhs.attributes))
+    return isinstance(rhs, Union) and self._tuple() == rhs._tuple()
+
+  def __lt__(self, rhs):
+    if not isinstance(self, type(rhs)):
+      return str(type(self)) < str(type(rhs))
+
+    return self._tuple() < rhs._tuple()
 
   def __hash__(self):
     return id(self)
@@ -707,8 +837,8 @@ class Array(ReferenceKind):
     length: The number of elements. None if unknown.
   """
 
-  ReferenceKind.AddSharedProperty('kind')
-  ReferenceKind.AddSharedProperty('length')
+  Kind.AddSharedProperty('kind')
+  Kind.AddSharedProperty('length')
 
   def __init__(self, kind=None, length=None):
     if kind is not None:
@@ -728,12 +858,11 @@ class Array(ReferenceKind):
       return '<%s spec=%r is_nullable=%r kind=%s length=%r>' % (
           self.__class__.__name__, self.spec, self.is_nullable, Repr(
               self.kind), self.length)
-    else:
-      return GenericRepr(self, {
-          'kind': True,
-          'length': False,
-          'is_nullable': False
-      })
+    return GenericRepr(self, {
+        'kind': True,
+        'length': False,
+        'is_nullable': False
+    })
 
   def __eq__(self, rhs):
     return (isinstance(rhs, Array)
@@ -754,8 +883,8 @@ class Map(ReferenceKind):
     key_kind: {Kind} The type of the keys. May be None.
     value_kind: {Kind} The type of the elements. May be None.
   """
-  ReferenceKind.AddSharedProperty('key_kind')
-  ReferenceKind.AddSharedProperty('value_kind')
+  Kind.AddSharedProperty('key_kind')
+  Kind.AddSharedProperty('value_kind')
 
   def __init__(self, key_kind=None, value_kind=None):
     if (key_kind is not None and value_kind is not None):
@@ -780,8 +909,7 @@ class Map(ReferenceKind):
       return '<%s spec=%r is_nullable=%r key_kind=%s value_kind=%s>' % (
           self.__class__.__name__, self.spec, self.is_nullable,
           Repr(self.key_kind), Repr(self.value_kind))
-    else:
-      return GenericRepr(self, {'key_kind': True, 'value_kind': True})
+    return GenericRepr(self, {'key_kind': True, 'value_kind': True})
 
   def __eq__(self, rhs):
     return (isinstance(rhs, Map) and
@@ -797,7 +925,7 @@ class Map(ReferenceKind):
 
 
 class PendingRemote(ReferenceKind):
-  ReferenceKind.AddSharedProperty('kind')
+  Kind.AddSharedProperty('kind')
 
   def __init__(self, kind=None):
     if kind is not None:
@@ -822,7 +950,7 @@ class PendingRemote(ReferenceKind):
 
 
 class PendingReceiver(ReferenceKind):
-  ReferenceKind.AddSharedProperty('kind')
+  Kind.AddSharedProperty('kind')
 
   def __init__(self, kind=None):
     if kind is not None:
@@ -847,7 +975,7 @@ class PendingReceiver(ReferenceKind):
 
 
 class PendingAssociatedRemote(ReferenceKind):
-  ReferenceKind.AddSharedProperty('kind')
+  Kind.AddSharedProperty('kind')
 
   def __init__(self, kind=None):
     if kind is not None:
@@ -873,7 +1001,7 @@ class PendingAssociatedRemote(ReferenceKind):
 
 
 class PendingAssociatedReceiver(ReferenceKind):
-  ReferenceKind.AddSharedProperty('kind')
+  Kind.AddSharedProperty('kind')
 
   def __init__(self, kind=None):
     if kind is not None:
@@ -899,7 +1027,7 @@ class PendingAssociatedReceiver(ReferenceKind):
 
 
 class InterfaceRequest(ReferenceKind):
-  ReferenceKind.AddSharedProperty('kind')
+  Kind.AddSharedProperty('kind')
 
   def __init__(self, kind=None):
     if kind is not None:
@@ -923,7 +1051,7 @@ class InterfaceRequest(ReferenceKind):
 
 
 class AssociatedInterfaceRequest(ReferenceKind):
-  ReferenceKind.AddSharedProperty('kind')
+  Kind.AddSharedProperty('kind')
 
   def __init__(self, kind=None):
     if kind is not None:
@@ -949,7 +1077,7 @@ class AssociatedInterfaceRequest(ReferenceKind):
             self.kind, rhs.kind)
 
 
-class Parameter(object):
+class Parameter:
   def __init__(self,
                mojom_name=None,
                kind=None,
@@ -983,7 +1111,7 @@ class Parameter(object):
                                       rhs.default, rhs.attributes))
 
 
-class Method(object):
+class Method:
   def __init__(self, interface, mojom_name, ordinal=None, attributes=None):
     self.interface = interface
     self.mojom_name = mojom_name
@@ -999,12 +1127,11 @@ class Method(object):
   def Repr(self, as_ref=True):
     if as_ref:
       return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)
-    else:
-      return GenericRepr(self, {
-          'mojom_name': False,
-          'parameters': True,
-          'response_parameters': True
-      })
+    return GenericRepr(self, {
+        'mojom_name': False,
+        'parameters': True,
+        'response_parameters': True
+    })
 
   def AddParameter(self,
                    mojom_name,
@@ -1061,21 +1188,32 @@ class Method(object):
     return self.attributes.get(ATTRIBUTE_UNLIMITED_SIZE) \
         if self.attributes else False
 
+  @property
+  def allowed_context(self):
+    return self.attributes.get(ATTRIBUTE_ALLOWED_CONTEXT) \
+        if self.attributes else None
+
+  def _tuple(self):
+    return (self.mojom_name, self.ordinal, self.parameters,
+            self.response_parameters, self.attributes)
+
   def __eq__(self, rhs):
-    return (isinstance(rhs, Method) and
-            (self.mojom_name, self.ordinal, self.parameters,
-             self.response_parameters,
-             self.attributes) == (rhs.mojom_name, rhs.ordinal, rhs.parameters,
-                                  rhs.response_parameters, rhs.attributes))
+    return isinstance(rhs, Method) and self._tuple() == rhs._tuple()
+
+  def __lt__(self, rhs):
+    if not isinstance(self, type(rhs)):
+      return str(type(self)) < str(type(rhs))
+
+    return self._tuple() < rhs._tuple()
 
 
 class Interface(ReferenceKind):
-  ReferenceKind.AddSharedProperty('mojom_name')
-  ReferenceKind.AddSharedProperty('name')
-  ReferenceKind.AddSharedProperty('methods')
-  ReferenceKind.AddSharedProperty('enums')
-  ReferenceKind.AddSharedProperty('constants')
-  ReferenceKind.AddSharedProperty('attributes')
+  Kind.AddSharedProperty('mojom_name')
+  Kind.AddSharedProperty('name')
+  Kind.AddSharedProperty('methods')
+  Kind.AddSharedProperty('enums')
+  Kind.AddSharedProperty('constants')
+  Kind.AddSharedProperty('attributes')
 
   def __init__(self, mojom_name=None, module=None, attributes=None):
     if mojom_name is not None:
@@ -1093,12 +1231,11 @@ class Interface(ReferenceKind):
   def Repr(self, as_ref=True):
     if as_ref:
       return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)
-    else:
-      return GenericRepr(self, {
-          'mojom_name': False,
-          'attributes': False,
-          'methods': False
-      })
+    return GenericRepr(self, {
+        'mojom_name': False,
+        'attributes': False,
+        'methods': False
+    })
 
   def AddMethod(self, mojom_name, ordinal=None, attributes=None):
     method = Method(self, mojom_name, ordinal, attributes)
@@ -1114,10 +1251,10 @@ class Interface(ReferenceKind):
     for constant in self.constants:
       constant.Stylize(stylizer)
 
-  def IsBackwardCompatible(self, older_interface, checker):
-    """This interface is backward-compatible with older_interface if and only
-    if all of the following conditions hold:
-      - All defined methods in older_interface (when identified by ordinal) have
+  def IsBackwardCompatible(self, rhs, checker):
+    """This interface is backward-compatible with rhs (older_interface) if and
+    only if all of the following conditions hold:
+      - All defined methods in rhs (when identified by ordinal) have
         backward-compatible definitions in this interface. For each method this
         means:
           - The parameter list is backward-compatible, according to backward-
@@ -1131,7 +1268,7 @@ class Interface(ReferenceKind):
             rules for structs.
       - All newly introduced methods in this interface have a [MinVersion]
         attribute specifying a version greater than any method in
-        older_interface.
+        rhs.
     """
 
     def buildOrdinalMethodMap(interface):
@@ -1144,7 +1281,7 @@ class Interface(ReferenceKind):
       return methods_by_ordinal
 
     new_methods = buildOrdinalMethodMap(self)
-    old_methods = buildOrdinalMethodMap(older_interface)
+    old_methods = buildOrdinalMethodMap(rhs)
     max_old_min_version = 0
     for ordinal, old_method in old_methods.items():
       new_method = new_methods.get(ordinal)
@@ -1186,6 +1323,27 @@ class Interface(ReferenceKind):
 
     return True
 
+  @property
+  def service_sandbox(self):
+    if not self.attributes:
+      return None
+    service_sandbox = self.attributes.get(ATTRIBUTE_SERVICE_SANDBOX, None)
+    if service_sandbox is None:
+      return None
+    # Constants are only allowed to refer to an enum here, so replace.
+    if isinstance(service_sandbox, Constant):
+      service_sandbox = service_sandbox.value
+    if not isinstance(service_sandbox, EnumValue):
+      raise Exception("ServiceSandbox attribute on %s must be an enum value." %
+                      self.module.name)
+    return service_sandbox
+
+  @property
+  def require_context(self):
+    if not self.attributes:
+      return None
+    return self.attributes.get(ATTRIBUTE_REQUIRE_CONTEXT, None)
+
   @property
   def stable(self):
     return self.attributes.get(ATTRIBUTE_STABLE, False) \
@@ -1199,11 +1357,18 @@ class Interface(ReferenceKind):
       prefix = self.module.GetNamespacePrefix()
     return '%s%s' % (prefix, self.mojom_name)
 
+  def _tuple(self):
+    return (self.mojom_name, self.methods, self.enums, self.constants,
+            self.attributes)
+
   def __eq__(self, rhs):
-    return (isinstance(rhs, Interface)
-            and (self.mojom_name, self.methods, self.enums, self.constants,
-                 self.attributes) == (rhs.mojom_name, rhs.methods, rhs.enums,
-                                      rhs.constants, rhs.attributes))
+    return isinstance(rhs, Interface) and self._tuple() == rhs._tuple()
+
+  def __lt__(self, rhs):
+    if not isinstance(self, type(rhs)):
+      return str(type(self)) < str(type(rhs))
+
+    return self._tuple() < rhs._tuple()
 
   @property
   def uuid(self):
@@ -1224,7 +1389,7 @@ class Interface(ReferenceKind):
 
 
 class AssociatedInterface(ReferenceKind):
-  ReferenceKind.AddSharedProperty('kind')
+  Kind.AddSharedProperty('kind')
 
   def __init__(self, kind=None):
     if kind is not None:
@@ -1249,7 +1414,7 @@ class AssociatedInterface(ReferenceKind):
                           self.kind, rhs.kind)
 
 
-class EnumField(object):
+class EnumField:
   def __init__(self,
                mojom_name=None,
                value=None,
@@ -1281,16 +1446,25 @@ class EnumField(object):
                                          rhs.attributes, rhs.numeric_value))
 
 
-class Enum(Kind):
+class Enum(ValueKind):
+  Kind.AddSharedProperty('mojom_name')
+  Kind.AddSharedProperty('name')
+  Kind.AddSharedProperty('native_only')
+  Kind.AddSharedProperty('fields')
+  Kind.AddSharedProperty('attributes')
+  Kind.AddSharedProperty('min_value')
+  Kind.AddSharedProperty('max_value')
+  Kind.AddSharedProperty('default_field')
+
   def __init__(self, mojom_name=None, module=None, attributes=None):
-    self.mojom_name = mojom_name
-    self.name = None
-    self.native_only = False
     if mojom_name is not None:
       spec = 'x:' + mojom_name
     else:
       spec = None
-    Kind.__init__(self, spec, module)
+    ValueKind.__init__(self, spec, False, module)
+    self.mojom_name = mojom_name
+    self.name = None
+    self.native_only = False
     self.fields = []
     self.attributes = attributes
     self.min_value = None
@@ -1300,8 +1474,7 @@ class Enum(Kind):
   def Repr(self, as_ref=True):
     if as_ref:
       return '<%s mojom_name=%r>' % (self.__class__.__name__, self.mojom_name)
-    else:
-      return GenericRepr(self, {'mojom_name': False, 'fields': False})
+    return GenericRepr(self, {'mojom_name': False, 'fields': False})
 
   def Stylize(self, stylizer):
     self.name = stylizer.StylizeEnum(self.mojom_name)
@@ -1327,14 +1500,14 @@ class Enum(Kind):
     return '%s%s' % (prefix, self.mojom_name)
 
   # pylint: disable=unused-argument
-  def IsBackwardCompatible(self, older_enum, checker):
-    """This enum is backward-compatible with older_enum if and only if one of
-    the following conditions holds:
+  def IsBackwardCompatible(self, rhs, checker):
+    """This enum is backward-compatible with rhs (older_enum) if and only if one
+    of the following conditions holds:
         - Neither enum is [Extensible] and both have the exact same set of valid
           numeric values. Field names and aliases for the same numeric value do
           not affect compatibility.
-        - older_enum is [Extensible], and for every version defined by
-          older_enum, this enum has the exact same set of valid numeric values.
+        - rhs is [Extensible], and for every version defined by
+          rhs, this enum has the exact same set of valid numeric values.
     """
 
     def buildVersionFieldMap(enum):
@@ -1345,10 +1518,10 @@ class Enum(Kind):
         fields_by_min_version[field.min_version].add(field.numeric_value)
       return fields_by_min_version
 
-    old_fields = buildVersionFieldMap(older_enum)
+    old_fields = buildVersionFieldMap(rhs)
     new_fields = buildVersionFieldMap(self)
 
-    if new_fields.keys() != old_fields.keys() and not older_enum.extensible:
+    if new_fields.keys() != old_fields.keys() and not rhs.extensible:
       return False
 
     for min_version, valid_values in old_fields.items():
@@ -1358,19 +1531,24 @@ class Enum(Kind):
 
     return True
 
+  def _tuple(self):
+    return (self.mojom_name, self.native_only, self.fields, self.attributes,
+            self.min_value, self.max_value, self.default_field)
+
   def __eq__(self, rhs):
-    return (isinstance(rhs, Enum) and
-            (self.mojom_name, self.native_only, self.fields, self.attributes,
-             self.min_value, self.max_value,
-             self.default_field) == (rhs.mojom_name, rhs.native_only,
-                                     rhs.fields, rhs.attributes, rhs.min_value,
-                                     rhs.max_value, rhs.default_field))
+    return isinstance(rhs, Enum) and self._tuple() == rhs._tuple()
+
+  def __lt__(self, rhs):
+    if not isinstance(self, type(rhs)):
+      return str(type(self)) < str(type(rhs))
+
+    return self._tuple() < rhs._tuple()
 
   def __hash__(self):
     return id(self)
 
 
-class Module(object):
+class Module:
   def __init__(self, path=None, mojom_namespace=None, attributes=None):
     self.path = path
     self.mojom_namespace = mojom_namespace
@@ -1380,11 +1558,11 @@ class Module(object):
     self.interfaces = []
     self.enums = []
     self.constants = []
-    self.kinds = {}
+    self.kinds = OrderedDict()
     self.attributes = attributes
     self.imports = []
-    self.imported_kinds = {}
-    self.metadata = {}
+    self.imported_kinds = OrderedDict()
+    self.metadata = OrderedDict()
 
   def __repr__(self):
     # Gives us a decent __repr__ for modules.
@@ -1405,16 +1583,15 @@ class Module(object):
     if as_ref:
       return '<%s path=%r mojom_namespace=%r>' % (
           self.__class__.__name__, self.path, self.mojom_namespace)
-    else:
-      return GenericRepr(
-          self, {
-              'path': False,
-              'mojom_namespace': False,
-              'attributes': False,
-              'structs': False,
-              'interfaces': False,
-              'unions': False
-          })
+    return GenericRepr(
+        self, {
+            'path': False,
+            'mojom_namespace': False,
+            'attributes': False,
+            'structs': False,
+            'interfaces': False,
+            'unions': False
+        })
 
   def GetNamespacePrefix(self):
     return '%s.' % self.mojom_namespace if self.mojom_namespace else ''
@@ -1451,7 +1628,7 @@ class Module(object):
       imported_module.Stylize(stylizer)
 
   def Dump(self, f):
-    pickle.dump(self, f, 2)
+    pickle.dump(self, f)
 
   @classmethod
   def Load(cls, f):
@@ -1461,15 +1638,15 @@ class Module(object):
 
 
 def IsBoolKind(kind):
-  return kind.spec == BOOL.spec
+  return kind.spec == BOOL.spec or kind.spec == NULLABLE_BOOL.spec
 
 
 def IsFloatKind(kind):
-  return kind.spec == FLOAT.spec
+  return kind.spec == FLOAT.spec or kind.spec == NULLABLE_FLOAT.spec
 
 
 def IsDoubleKind(kind):
-  return kind.spec == DOUBLE.spec
+  return kind.spec == DOUBLE.spec or kind.spec == NULLABLE_DOUBLE.spec
 
 
 def IsIntegralKind(kind):
@@ -1477,7 +1654,14 @@ def IsIntegralKind(kind):
           or kind.spec == INT16.spec or kind.spec == INT32.spec
           or kind.spec == INT64.spec or kind.spec == UINT8.spec
           or kind.spec == UINT16.spec or kind.spec == UINT32.spec
-          or kind.spec == UINT64.spec)
+          or kind.spec == UINT64.spec or kind.spec == NULLABLE_BOOL.spec
+          or kind.spec == NULLABLE_INT8.spec or kind.spec == NULLABLE_INT16.spec
+          or kind.spec == NULLABLE_INT32.spec
+          or kind.spec == NULLABLE_INT64.spec
+          or kind.spec == NULLABLE_UINT8.spec
+          or kind.spec == NULLABLE_UINT16.spec
+          or kind.spec == NULLABLE_UINT32.spec
+          or kind.spec == NULLABLE_UINT64.spec)
 
 
 def IsStringKind(kind):
@@ -1563,7 +1747,7 @@ def IsReferenceKind(kind):
 
 
 def IsNullableKind(kind):
-  return IsReferenceKind(kind) and kind.is_nullable
+  return kind.is_nullable
 
 
 def IsMapKind(kind):
@@ -1664,11 +1848,8 @@ def MethodPassesInterfaces(method):
   return _AnyMethodParameterRecursive(method, IsInterfaceKind)
 
 
-def HasSyncMethods(interface):
-  for method in interface.methods:
-    if method.sync:
-      return True
-  return False
+def GetSyncMethodOrdinals(interface):
+  return [method.ordinal for method in interface.methods if method.sync]
 
 
 def HasUninterruptableMethods(interface):
@@ -1700,18 +1881,17 @@ def ContainsHandlesOrInterfaces(kind):
     checked.add(kind.spec)
     if IsStructKind(kind):
       return any(Check(field.kind) for field in kind.fields)
-    elif IsUnionKind(kind):
+    if IsUnionKind(kind):
       return any(Check(field.kind) for field in kind.fields)
-    elif IsAnyHandleKind(kind):
+    if IsAnyHandleKind(kind):
       return True
-    elif IsAnyInterfaceKind(kind):
+    if IsAnyInterfaceKind(kind):
       return True
-    elif IsArrayKind(kind):
+    if IsArrayKind(kind):
       return Check(kind.kind)
-    elif IsMapKind(kind):
+    if IsMapKind(kind):
       return Check(kind.key_kind) or Check(kind.value_kind)
-    else:
-      return False
+    return False
 
   return Check(kind)
 
@@ -1738,21 +1918,20 @@ def ContainsNativeTypes(kind):
     checked.add(kind.spec)
     if IsEnumKind(kind):
       return kind.native_only
-    elif IsStructKind(kind):
+    if IsStructKind(kind):
       if kind.native_only:
         return True
       if any(enum.native_only for enum in kind.enums):
         return True
       return any(Check(field.kind) for field in kind.fields)
-    elif IsUnionKind(kind):
+    if IsUnionKind(kind):
       return any(Check(field.kind) for field in kind.fields)
-    elif IsInterfaceKind(kind):
+    if IsInterfaceKind(kind):
       return any(enum.native_only for enum in kind.enums)
-    elif IsArrayKind(kind):
+    if IsArrayKind(kind):
       return Check(kind.kind)
-    elif IsMapKind(kind):
+    if IsMapKind(kind):
       return Check(kind.key_kind) or Check(kind.value_kind)
-    else:
-      return False
+    return False
 
   return Check(kind)
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py
index e8fd4936..2a4e852c 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py
index 88b77c98..71011109 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack.py
@@ -1,7 +1,8 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import copy
 from mojom.generate import module as mojom
 
 # This module provides a mechanism for determining the packed order and offsets
@@ -15,7 +16,7 @@ from mojom.generate import module as mojom
 HEADER_SIZE = 8
 
 
-class PackedField(object):
+class PackedField:
   kind_to_size = {
       mojom.BOOL: 1,
       mojom.INT8: 1,
@@ -75,18 +76,55 @@ class PackedField(object):
       return 8
     return cls.GetSizeForKind(kind)
 
-  def __init__(self, field, index, ordinal):
+  def __init__(self,
+               field,
+               index,
+               ordinal,
+               original_field=None,
+               sub_ordinal=None,
+               linked_value_packed_field=None):
     """
     Args:
       field: the original field.
       index: the position of the original field in the struct.
       ordinal: the ordinal of the field for serialization.
+      original_field: See below.
+      sub_ordinal: See below.
+      linked_value_packed_field: See below.
+
+    original_field, sub_ordinal, and linked_value_packed_field are used to
+    support nullable ValueKind fields. For legacy reasons, nullable ValueKind
+    fields actually generate two PackedFields. This allows:
+
+    - backwards compatibility prior to Mojo support for nullable ValueKinds.
+    - correct packing of fields for the aforementioned backwards compatibility.
+
+    When translating Fields to PackedFields, the original field is turned into
+    two PackedFields: the first PackedField always has type mojom.BOOL, while
+    the second PackedField has the non-nullable version of the field's kind.
+
+    When constructing these PackedFields, original_field references the field
+    as defined in the mojom; the name as defined in the mojom will be used for
+    all layers above the wire/data layer.
+
+    sub_ordinal is used to sort the two PackedFields correctly with respect to
+    each other: the first mojom.BOOL field always has sub_ordinal 0, while the
+    second field always has sub_ordinal 1.
+
+    Finally, linked_value_packed_field is used by the serialization and
+    deserialization helpers, which generally just iterate over a PackedStruct's
+    PackedField's in ordinal order. This allows the helpers to easily reference
+    any related PackedFields rather than having to lookup related PackedFields
+    by index while iterating.
     """
     self.field = field
     self.index = index
     self.ordinal = ordinal
-    self.size = self.GetSizeForKind(field.kind)
-    self.alignment = self.GetAlignmentForKind(field.kind)
+    self.original_field = original_field
+    self.sub_ordinal = sub_ordinal
+    self.linked_value_packed_field = linked_value_packed_field
+    self.size = self.GetSizeForKind(self.field.kind)
+    self.alignment = self.GetAlignmentForKind(self.field.kind)
     self.offset = None
     self.bit = None
     self.min_version = None
@@ -120,7 +158,33 @@ def GetPayloadSizeUpToField(field):
   return offset + pad
 
 
-class PackedStruct(object):
+def IsNullableValueKindPackedField(field):
+  """Returns true if `field` is derived from a nullable ValueKind field.
+
+  Nullable ValueKind fields often require special handling in the bindings due
+  to the way the implementation is constrained for wire compatibility.
+  """
+  assert isinstance(field, PackedField)
+  return field.sub_ordinal is not None
+
+
+def IsPrimaryNullableValueKindPackedField(field):
+  """Returns true if `field` is derived from a nullable ValueKind mojom field
+  and is the "primary" field.
+
+  The primary field is a bool PackedField that controls if the field should be
+  considered as present or not; it will have a reference to the PackedField that
+  holds the actual value representation if considered present.
+
+  Bindings code that translates between the wire protocol and the higher layers
+  can use this to simplify mapping multiple PackedFields to the single field
+  that is logically exposed to bindings consumers.
+  """
+  assert isinstance(field, PackedField)
+  return field.linked_value_packed_field is not None
+
+
+class PackedStruct:
   def __init__(self, struct):
     self.struct = struct
     # |packed_fields| contains all the fields, in increasing offset order.
@@ -139,9 +203,41 @@ class PackedStruct(object):
     for index, field in enumerate(struct.fields):
       if field.ordinal is not None:
         ordinal = field.ordinal
-      src_fields.append(PackedField(field, index, ordinal))
+      # Nullable value types are a bit weird: they generate two PackedFields
+      # despite being a single ValueKind. This is for wire compatibility to
+      # ease the transition from legacy mojom syntax where nullable value types
+      # were not supported.
+      if isinstance(field.kind, mojom.ValueKind) and field.kind.is_nullable:
+        # The suffixes intentionally use Unicode codepoints which are considered
+        # valid C++/Java/JavaScript identifiers, yet are unlikely to be used in
+        # actual user code.
+        has_value_field = copy.copy(field)
+        has_value_field.name = f'{field.mojom_name}_$flag'
+        has_value_field.kind = mojom.BOOL
+
+        value_field = copy.copy(field)
+        value_field.name = f'{field.mojom_name}_$value'
+        value_field.kind = field.kind.MakeUnnullableKind()
+
+        value_packed_field = PackedField(value_field,
+                                         index,
+                                         ordinal,
+                                         original_field=field,
+                                         sub_ordinal=1,
+                                         linked_value_packed_field=None)
+        has_value_packed_field = PackedField(
+            has_value_field,
+            index,
+            ordinal,
+            original_field=field,
+            sub_ordinal=0,
+            linked_value_packed_field=value_packed_field)
+        src_fields.append(has_value_packed_field)
+        src_fields.append(value_packed_field)
+      else:
+        src_fields.append(PackedField(field, index, ordinal))
       ordinal += 1
-    src_fields.sort(key=lambda field: field.ordinal)
+    src_fields.sort(key=lambda field: (field.ordinal, field.sub_ordinal))
 
     # Set |min_version| for each field.
     next_min_version = 0
@@ -156,10 +252,11 @@ class PackedStruct(object):
       if (packed_field.min_version != 0
           and mojom.IsReferenceKind(packed_field.field.kind)
           and not packed_field.field.kind.is_nullable):
-        raise Exception("Non-nullable fields are only allowed in version 0 of "
-                        "a struct. %s.%s is defined with [MinVersion=%d]." %
-                        (self.struct.name, packed_field.field.name,
-                         packed_field.min_version))
+        raise Exception(
+            "Non-nullable reference fields are only allowed in version 0 of a "
+            "struct. %s.%s is defined with [MinVersion=%d]." %
+            (self.struct.name, packed_field.field.name,
+             packed_field.min_version))
 
     src_field = src_fields[0]
     src_field.offset = 0
@@ -186,7 +283,7 @@ class PackedStruct(object):
         dst_fields.append(src_field)
 
 
-class ByteInfo(object):
+class ByteInfo:
   def __init__(self):
     self.is_padding = False
     self.packed_fields = []
@@ -214,7 +311,7 @@ def GetByteLayout(packed_struct):
   return byte_info
 
 
-class VersionInfo(object):
+class VersionInfo:
   def __init__(self, version, num_fields, num_bytes):
     self.version = version
     self.num_fields = num_fields
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py
index 98c705ad..5c6c36d5 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/pack_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --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
index 0da90058..807e2a4f 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py
@@ -1,4 +1,4 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py
index 7580b780..71ce3c03 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py
@@ -1,4 +1,4 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Convert parse tree to AST.
@@ -12,17 +12,296 @@ already been parsed and converted to ASTs before.
 import itertools
 import os
 import re
-import sys
 
+from collections import OrderedDict
 from mojom.generate import generator
 from mojom.generate import module as mojom
 from mojom.parse import ast
 
 
-def _IsStrOrUnicode(x):
-  if sys.version_info[0] < 3:
-    return isinstance(x, (unicode, str))
-  return isinstance(x, str)
+is_running_backwards_compatibility_check_hack = False
+
+### DO NOT ADD ENTRIES TO THIS LIST. ###
+_EXTENSIBLE_ENUMS_MISSING_DEFAULT = (
+    'x:arc.keymaster.mojom.Algorithm',
+    'x:arc.keymaster.mojom.Digest',
+    'x:arc.keymaster.mojom.SignatureResult',
+    'x:arc.mojom.AccessibilityActionType',
+    'x:arc.mojom.AccessibilityBooleanProperty',
+    'x:arc.mojom.AccessibilityEventIntListProperty',
+    'x:arc.mojom.AccessibilityEventIntProperty',
+    'x:arc.mojom.AccessibilityEventStringProperty',
+    'x:arc.mojom.AccessibilityEventType',
+    'x:arc.mojom.AccessibilityFilterType',
+    'x:arc.mojom.AccessibilityIntListProperty',
+    'x:arc.mojom.AccessibilityIntProperty',
+    'x:arc.mojom.AccessibilityLiveRegionType',
+    'x:arc.mojom.AccessibilityNotificationStateType',
+    'x:arc.mojom.AccessibilityRangeType',
+    'x:arc.mojom.AccessibilitySelectionMode',
+    'x:arc.mojom.AccessibilityStringListProperty',
+    'x:arc.mojom.AccessibilityStringProperty',
+    'x:arc.mojom.AccessibilityWindowBooleanProperty',
+    'x:arc.mojom.AccessibilityWindowIntListProperty',
+    'x:arc.mojom.AccessibilityWindowIntProperty',
+    'x:arc.mojom.AccessibilityWindowStringProperty',
+    'x:arc.mojom.AccessibilityWindowType',
+    'x:arc.mojom.AccountCheckStatus',
+    'x:arc.mojom.AccountUpdateType',
+    'x:arc.mojom.ActionType',
+    'x:arc.mojom.Algorithm',
+    'x:arc.mojom.AndroidIdSource',
+    'x:arc.mojom.AnrSource',
+    'x:arc.mojom.AnrType',
+    'x:arc.mojom.AppDiscoveryRequestState',
+    'x:arc.mojom.AppKillType',
+    'x:arc.mojom.AppPermission',
+    'x:arc.mojom.AppPermissionGroup',
+    'x:arc.mojom.AppReinstallState',
+    'x:arc.mojom.AppShortcutItemType',
+    'x:arc.mojom.ArcAuthCodeStatus',
+    'x:arc.mojom.ArcClipboardDragDropEvent',
+    'x:arc.mojom.ArcCorePriAbiMigEvent',
+    'x:arc.mojom.ArcDnsQuery',
+    'x:arc.mojom.ArcImageCopyPasteCompatAction',
+    'x:arc.mojom.ArcNetworkError',
+    'x:arc.mojom.ArcNetworkEvent',
+    'x:arc.mojom.ArcNotificationEvent',
+    'x:arc.mojom.ArcNotificationExpandState',
+    'x:arc.mojom.ArcNotificationPriority',
+    'x:arc.mojom.ArcNotificationRemoteInputState',
+    'x:arc.mojom.ArcNotificationShownContents',
+    'x:arc.mojom.ArcNotificationStyle',
+    'x:arc.mojom.ArcNotificationType',
+    'x:arc.mojom.ArcPipEvent',
+    'x:arc.mojom.ArcResizeLockState',
+    'x:arc.mojom.ArcSignInSuccess',
+    'x:arc.mojom.ArcTimerResult',
+    'x:arc.mojom.AudioSwitch',
+    'x:arc.mojom.BluetoothAclState',
+    'x:arc.mojom.BluetoothAdapterState',
+    'x:arc.mojom.BluetoothAdvertisingDataType',
+    'x:arc.mojom.BluetoothBondState',
+    'x:arc.mojom.BluetoothDeviceType',
+    'x:arc.mojom.BluetoothDiscoveryState',
+    'x:arc.mojom.BluetoothGattDBAttributeType',
+    'x:arc.mojom.BluetoothGattStatus',
+    'x:arc.mojom.BluetoothPropertyType',
+    'x:arc.mojom.BluetoothScanMode',
+    'x:arc.mojom.BluetoothSdpAttributeType',
+    'x:arc.mojom.BluetoothSocketType',
+    'x:arc.mojom.BluetoothStatus',
+    'x:arc.mojom.BootType',
+    'x:arc.mojom.CaptionTextShadowType',
+    'x:arc.mojom.ChangeType',
+    'x:arc.mojom.ChromeAccountType',
+    'x:arc.mojom.ChromeApp',
+    'x:arc.mojom.ChromePage',
+    'x:arc.mojom.ClockId',
+    'x:arc.mojom.CloudProvisionFlowError',
+    'x:arc.mojom.CommandResultType',
+    'x:arc.mojom.CompanionLibApiId',
+    'x:arc.mojom.ConnectionStateType',
+    'x:arc.mojom.ContentChangeType',
+    'x:arc.mojom.CpuRestrictionState',
+    'x:arc.mojom.CursorCoordinateSpace',
+    'x:arc.mojom.DataRestoreStatus',
+    'x:arc.mojom.DecoderStatus',
+    'x:arc.mojom.DeviceType',
+    'x:arc.mojom.Digest',
+    'x:arc.mojom.DisplayWakeLockType',
+    'x:arc.mojom.EapMethod',
+    'x:arc.mojom.EapPhase2Method',
+    'x:arc.mojom.FileSelectorEventType',
+    'x:arc.mojom.GMSCheckInError',
+    'x:arc.mojom.GMSSignInError',
+    'x:arc.mojom.GeneralSignInError',
+    'x:arc.mojom.GetNetworksRequestType',
+    'x:arc.mojom.HalPixelFormat',
+    'x:arc.mojom.IPAddressType',
+    'x:arc.mojom.InstallErrorReason',
+    'x:arc.mojom.KeyFormat',
+    'x:arc.mojom.KeyManagement',
+    'x:arc.mojom.KeyPurpose',
+    'x:arc.mojom.KeymasterError',
+    'x:arc.mojom.MainAccountHashMigrationStatus',
+    'x:arc.mojom.MainAccountResolutionStatus',
+    'x:arc.mojom.ManagementChangeStatus',
+    'x:arc.mojom.ManagementState',
+    'x:arc.mojom.MessageCenterVisibility',
+    'x:arc.mojom.MetricsType',
+    'x:arc.mojom.MountEvent',
+    'x:arc.mojom.NativeBridgeType',
+    'x:arc.mojom.NetworkResult',
+    'x:arc.mojom.NetworkType',
+    'x:arc.mojom.OemCryptoAlgorithm',
+    'x:arc.mojom.OemCryptoCipherMode',
+    'x:arc.mojom.OemCryptoHdcpCapability',
+    'x:arc.mojom.OemCryptoLicenseType',
+    'x:arc.mojom.OemCryptoPrivateKey',
+    'x:arc.mojom.OemCryptoProvisioningMethod',
+    'x:arc.mojom.OemCryptoResult',
+    'x:arc.mojom.OemCryptoRsaPaddingScheme',
+    'x:arc.mojom.OemCryptoUsageEntryStatus',
+    'x:arc.mojom.Padding',
+    'x:arc.mojom.PaiFlowState',
+    'x:arc.mojom.PatternType',
+    'x:arc.mojom.PressureLevel',
+    'x:arc.mojom.PrintColorMode',
+    'x:arc.mojom.PrintContentType',
+    'x:arc.mojom.PrintDuplexMode',
+    'x:arc.mojom.PrinterStatus',
+    'x:arc.mojom.ProcessState',
+    'x:arc.mojom.PurchaseState',
+    'x:arc.mojom.ReauthReason',
+    'x:arc.mojom.ScaleFactor',
+    'x:arc.mojom.SecurityType',
+    'x:arc.mojom.SegmentStyle',
+    'x:arc.mojom.SelectFilesActionType',
+    'x:arc.mojom.SetNativeChromeVoxResponse',
+    'x:arc.mojom.ShareFiles',
+    'x:arc.mojom.ShowPackageInfoPage',
+    'x:arc.mojom.SpanType',
+    'x:arc.mojom.SupportedLinkChangeSource',
+    'x:arc.mojom.TetheringClientState',
+    'x:arc.mojom.TextInputType',
+    'x:arc.mojom.TtsEventType',
+    'x:arc.mojom.VideoCodecProfile',
+    'x:arc.mojom.VideoDecodeAccelerator.Result',
+    'x:arc.mojom.VideoEncodeAccelerator.Error',
+    'x:arc.mojom.VideoFrameStorageType',
+    'x:arc.mojom.VideoPixelFormat',
+    'x:arc.mojom.WakefulnessMode',
+    'x:arc.mojom.WebApkInstallResult',
+    'x:ash.ime.mojom.InputFieldType',
+    'x:ash.ime.mojom.PersonalizationMode',
+    'x:ash.language.mojom.FeatureId',
+    'x:blink.mojom.ScrollRestorationType',
+    'x:chrome_cleaner.mojom.PromptAcceptance',
+    'x:chromeos.cdm.mojom.CdmKeyStatus',
+    'x:chromeos.cdm.mojom.CdmMessageType',
+    'x:chromeos.cdm.mojom.CdmSessionType',
+    'x:chromeos.cdm.mojom.DecryptStatus',
+    'x:chromeos.cdm.mojom.EmeInitDataType',
+    'x:chromeos.cdm.mojom.EncryptionScheme',
+    'x:chromeos.cdm.mojom.HdcpVersion',
+    'x:chromeos.cdm.mojom.OutputProtection.LinkType',
+    'x:chromeos.cdm.mojom.OutputProtection.ProtectionType',
+    'x:chromeos.cdm.mojom.PromiseException',
+    'x:chromeos.cfm.mojom.EnqueuePriority',
+    'x:chromeos.cfm.mojom.LoggerErrorCode',
+    'x:chromeos.cfm.mojom.LoggerState',
+    'x:chromeos.cros_healthd.mojom.CryptoAlgorithm',
+    'x:chromeos.cros_healthd.mojom.EncryptionState',
+    'x:chromeos.machine_learning.mojom.AnnotationUsecase',
+    'x:chromeos.machine_learning.mojom.BuiltinModelId',
+    'x:chromeos.machine_learning.mojom.CreateGraphExecutorResult',
+    'x:chromeos.machine_learning.mojom.DocumentScannerResultStatus',
+    'x:chromeos.machine_learning.mojom.EndpointReason',
+    'x:chromeos.machine_learning.mojom.EndpointerType',
+    'x:chromeos.machine_learning.mojom.ExecuteResult',
+    'x:chromeos.machine_learning.mojom.GrammarCheckerResult.Status',
+    'x:chromeos.machine_learning.mojom.HandwritingRecognizerResult.Status',
+    'x:chromeos.machine_learning.mojom.LoadHandwritingModelResult',
+    'x:chromeos.machine_learning.mojom.LoadModelResult',
+    'x:chromeos.machine_learning.mojom.Rotation',
+    'x:chromeos.network_config.mojom.ConnectionStateType',
+    'x:chromeos.network_config.mojom.DeviceStateType',
+    'x:chromeos.network_config.mojom.IPConfigType',
+    'x:chromeos.network_config.mojom.NetworkType',
+    'x:chromeos.network_config.mojom.OncSource',
+    'x:chromeos.network_config.mojom.PolicySource',
+    'x:chromeos.network_config.mojom.PortalState',
+    'x:chromeos.wilco_dtc_supportd.mojom.WilcoDtcSupportdEvent',
+    'x:chromeos.wilco_dtc_supportd.mojom.WilcoDtcSupportdWebRequestHttpMethod',
+    'x:chromeos.wilco_dtc_supportd.mojom.WilcoDtcSupportdWebRequestStatus',
+    'x:cros.mojom.CameraClientType',
+    'x:cros.mojom.CameraMetadataSectionStart',
+    'x:cros.mojom.CameraMetadataTag',
+    'x:cros.mojom.HalPixelFormat',
+    'x:crosapi.mojom.AllowedPaths',
+    'x:crosapi.mojom.BrowserAppInstanceType',
+    'x:crosapi.mojom.CreationResult',
+    'x:crosapi.mojom.DeviceAccessResultCode',
+    'x:crosapi.mojom.DeviceMode',
+    'x:crosapi.mojom.DlpRestrictionLevel',
+    'x:crosapi.mojom.ExoImeSupport',
+    'x:crosapi.mojom.FullscreenVisibility',
+    'x:crosapi.mojom.GoogleServiceAuthError.State',
+    'x:crosapi.mojom.IsInstallableResult',
+    'x:crosapi.mojom.KeyTag',
+    'x:crosapi.mojom.KeystoreSigningAlgorithmName',
+    'x:crosapi.mojom.KeystoreType',
+    'x:crosapi.mojom.LacrosFeedbackSource',
+    'x:crosapi.mojom.MemoryPressureLevel',
+    'x:crosapi.mojom.MetricsReportingManaged',
+    'x:crosapi.mojom.NotificationType',
+    'x:crosapi.mojom.OndeviceHandwritingSupport',
+    'x:crosapi.mojom.OpenResult',
+    'x:crosapi.mojom.PolicyDomain',
+    'x:crosapi.mojom.RegistrationCodeType',
+    'x:crosapi.mojom.ScaleFactor',
+    'x:crosapi.mojom.SearchResult.OptionalBool',
+    'x:crosapi.mojom.SelectFileDialogType',
+    'x:crosapi.mojom.SelectFileResult',
+    'x:crosapi.mojom.SharesheetResult',
+    'x:crosapi.mojom.TouchEventType',
+    'x:crosapi.mojom.VideoRotation',
+    'x:crosapi.mojom.WallpaperLayout',
+    'x:crosapi.mojom.WebAppInstallResultCode',
+    'x:crosapi.mojom.WebAppUninstallResultCode',
+    'x:device.mojom.HidBusType',
+    'x:device.mojom.WakeLockReason',
+    'x:device.mojom.WakeLockType',
+    'x:drivefs.mojom.DialogReason.Type',
+    'x:drivefs.mojom.DriveError.Type',
+    'x:drivefs.mojom.DriveFsDelegate.ExtensionConnectionStatus',
+    'x:drivefs.mojom.FileMetadata.CanPinStatus',
+    'x:drivefs.mojom.FileMetadata.Type',
+    'x:drivefs.mojom.ItemEventReason',
+    'x:drivefs.mojom.MirrorPathStatus',
+    'x:drivefs.mojom.MirrorSyncStatus',
+    'x:drivefs.mojom.QueryParameters.SortField',
+    'x:fuzz.mojom.FuzzEnum',
+    'x:media.mojom.FillLightMode',
+    'x:media.mojom.MeteringMode',
+    'x:media.mojom.PowerLineFrequency',
+    'x:media.mojom.RedEyeReduction',
+    'x:media.mojom.ResolutionChangePolicy',
+    'x:media.mojom.VideoCaptureApi',
+    'x:media.mojom.VideoCaptureBufferType',
+    'x:media.mojom.VideoCaptureError',
+    'x:media.mojom.VideoCaptureFrameDropReason',
+    'x:media.mojom.VideoCapturePixelFormat',
+    'x:media.mojom.VideoCaptureTransportType',
+    'x:media.mojom.VideoFacingMode',
+    'x:media_session.mojom.AudioFocusType',
+    'x:media_session.mojom.CameraState',
+    'x:media_session.mojom.EnforcementMode',
+    'x:media_session.mojom.MediaAudioVideoState',
+    'x:media_session.mojom.MediaImageBitmapColorType',
+    'x:media_session.mojom.MediaPictureInPictureState',
+    'x:media_session.mojom.MediaPlaybackState',
+    'x:media_session.mojom.MediaSession.SuspendType',
+    'x:media_session.mojom.MediaSessionAction',
+    'x:media_session.mojom.MediaSessionImageType',
+    'x:media_session.mojom.MediaSessionInfo.SessionState',
+    'x:media_session.mojom.MicrophoneState',
+    'x:ml.model_loader.mojom.ComputeResult',
+    'x:ml.model_loader.mojom.CreateModelLoaderResult',
+    'x:ml.model_loader.mojom.LoadModelResult',
+    'x:mojo.test.AnExtensibleEnum',
+    'x:mojo.test.EnumB',
+    'x:mojo.test.ExtensibleEmptyEnum',
+    'x:mojo.test.enum_default_unittest.mojom.ExtensibleEnumWithoutDefault',
+    'x:network.mojom.WebSandboxFlags',
+    'x:payments.mojom.BillingResponseCode',
+    'x:payments.mojom.CreateDigitalGoodsResponseCode',
+    'x:payments.mojom.ItemType',
+    'x:printing.mojom.PrinterType',
+    'x:ui.mojom.KeyboardCode',
+)
+### DO NOT ADD ENTRIES TO THIS LIST. ###
 
 
 def _DuplicateName(values):
@@ -98,12 +377,6 @@ def _MapKind(kind):
   }
   if kind.endswith('?'):
     base_kind = _MapKind(kind[0:-1])
-    # NOTE: This doesn't rule out enum types. Those will be detected later, when
-    # cross-reference is established.
-    reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso', 'rmt', 'rcv',
-                       'rma', 'rca')
-    if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:
-      raise Exception('A type (spec "%s") cannot be made nullable' % base_kind)
     return '?' + base_kind
   if kind.endswith('}'):
     lbracket = kind.rfind('{')
@@ -113,8 +386,6 @@ def _MapKind(kind):
     lbracket = kind.rfind('[')
     typename = kind[0:lbracket]
     return 'a' + kind[lbracket + 1:-1] + ':' + _MapKind(typename)
-  if kind.endswith('&'):
-    return 'r:' + _MapKind(kind[0:-1])
   if kind.startswith('asso<'):
     assert kind.endswith('>')
     return 'asso:' + _MapKind(kind[5:-1])
@@ -135,13 +406,35 @@ def _MapKind(kind):
   return 'x:' + kind
 
 
-def _AttributeListToDict(attribute_list):
+def _MapValueToEnum(module, value):
+  # True/False/None
+  if value is None:
+    return value
+  if not isinstance(value, str):
+    return value
+  # Otherwise try to find it.
+  try:
+    trial = _LookupValue(module, None, None, ('IDENTIFIER', value))
+    if isinstance(trial, mojom.ConstantValue):
+      return trial.constant
+    if isinstance(trial, mojom.EnumValue):
+      return trial
+  except ValueError:
+    pass
+  # Return the string if it did not resolve to a constant or enum.
+  return value
+
+
+def _AttributeListToDict(module, attribute_list):
   if attribute_list is None:
     return None
   assert isinstance(attribute_list, ast.AttributeList)
-  # TODO(vtl): Check for duplicate keys here.
-  return dict(
-      [(attribute.key, attribute.value) for attribute in attribute_list])
+  attributes = dict()
+  for attribute in attribute_list:
+    if attribute.key in attributes:
+      raise Exception("Duplicate key (%s) in attribute list" % attribute.key)
+    attributes[attribute.key] = _MapValueToEnum(module, attribute.value)
+  return attributes
 
 
 builtin_values = frozenset([
@@ -257,7 +550,8 @@ def _Kind(kinds, spec, scope):
     return kind
 
   if spec.startswith('?'):
-    kind = _Kind(kinds, spec[1:], scope).MakeNullableKind()
+    kind = _Kind(kinds, spec[1:], scope)
+    kind = kind.MakeNullableKind()
   elif spec.startswith('a:'):
     kind = mojom.Array(_Kind(kinds, spec[2:], scope))
   elif spec.startswith('asso:'):
@@ -345,7 +639,7 @@ def _Struct(module, parsed_struct):
             struct.fields_data.append,
         })
 
-  struct.attributes = _AttributeListToDict(parsed_struct.attribute_list)
+  struct.attributes = _AttributeListToDict(module, parsed_struct.attribute_list)
 
   # Enforce that a [Native] attribute is set to make native-only struct
   # declarations more explicit.
@@ -377,7 +671,7 @@ def _Union(module, parsed_union):
   union.fields_data = []
   _ProcessElements(parsed_union.mojom_name, parsed_union.body,
                    {ast.UnionField: union.fields_data.append})
-  union.attributes = _AttributeListToDict(parsed_union.attribute_list)
+  union.attributes = _AttributeListToDict(module, parsed_union.attribute_list)
   return union
 
 
@@ -398,7 +692,7 @@ def _StructField(module, parsed_field, struct):
   field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
   field.default = _LookupValue(module, struct, field.kind,
                                parsed_field.default_value)
-  field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+  field.attributes = _AttributeListToDict(module, parsed_field.attribute_list)
   return field
 
 
@@ -414,11 +708,21 @@ def _UnionField(module, parsed_field, union):
   """
   field = mojom.UnionField()
   field.mojom_name = parsed_field.mojom_name
+  # Disallow unions from being self-recursive.
+  parsed_typename = parsed_field.typename
+  if parsed_typename.endswith('?'):
+    parsed_typename = parsed_typename[:-1]
+  assert parsed_typename != union.mojom_name
   field.kind = _Kind(module.kinds, _MapKind(parsed_field.typename),
                      (module.mojom_namespace, union.mojom_name))
   field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
   field.default = None
-  field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+  field.attributes = _AttributeListToDict(module, parsed_field.attribute_list)
+  if field.is_default and not mojom.IsNullableKind(field.kind) and \
+     not mojom.IsIntegralKind(field.kind):
+    raise Exception(
+        '[Default] field for union %s must be nullable or integral type.' %
+        union.mojom_name)
   return field
 
 
@@ -439,7 +743,8 @@ def _Parameter(module, parsed_param, interface):
   parameter.ordinal = (parsed_param.ordinal.value
                        if parsed_param.ordinal else None)
   parameter.default = None  # TODO(tibell): We never have these. Remove field?
-  parameter.attributes = _AttributeListToDict(parsed_param.attribute_list)
+  parameter.attributes = _AttributeListToDict(module,
+                                              parsed_param.attribute_list)
   return parameter
 
 
@@ -464,7 +769,7 @@ def _Method(module, parsed_method, interface):
     method.response_parameters = list(
         map(lambda parameter: _Parameter(module, parameter, interface),
             parsed_method.response_parameter_list))
-  method.attributes = _AttributeListToDict(parsed_method.attribute_list)
+  method.attributes = _AttributeListToDict(module, parsed_method.attribute_list)
 
   # Enforce that only methods with response can have a [Sync] attribute.
   if method.sync and method.response_parameters is None:
@@ -492,7 +797,8 @@ def _Interface(module, parsed_iface):
   interface.mojom_name = parsed_iface.mojom_name
   interface.spec = 'x:' + module.GetNamespacePrefix() + interface.mojom_name
   module.kinds[interface.spec] = interface
-  interface.attributes = _AttributeListToDict(parsed_iface.attribute_list)
+  interface.attributes = _AttributeListToDict(module,
+                                              parsed_iface.attribute_list)
   interface.enums = []
   interface.constants = []
   interface.methods_data = []
@@ -522,7 +828,7 @@ def _EnumField(module, enum, parsed_field):
   field = mojom.EnumField()
   field.mojom_name = parsed_field.mojom_name
   field.value = _LookupValue(module, enum, None, parsed_field.value)
-  field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+  field.attributes = _AttributeListToDict(module, parsed_field.attribute_list)
   value = mojom.EnumValue(module, enum, field)
   module.values[value.GetSpec()] = value
   return field
@@ -544,7 +850,7 @@ def _ResolveNumericEnumValues(enum):
       prev_value += 1
 
     # Integral value (e.g: BEGIN = -0x1).
-    elif _IsStrOrUnicode(field.value):
+    elif isinstance(field.value, str):
       prev_value = int(field.value, 0)
 
     # Reference to a previous enum value (e.g: INIT = BEGIN).
@@ -588,7 +894,7 @@ def _Enum(module, parsed_enum, parent_kind):
     mojom_name = parent_kind.mojom_name + '.' + mojom_name
   enum.spec = 'x:%s.%s' % (module.mojom_namespace, mojom_name)
   enum.parent_kind = parent_kind
-  enum.attributes = _AttributeListToDict(parsed_enum.attribute_list)
+  enum.attributes = _AttributeListToDict(module, parsed_enum.attribute_list)
 
   if not enum.native_only:
     enum.fields = list(
@@ -600,11 +906,18 @@ def _Enum(module, parsed_enum, parent_kind):
     for field in enum.fields:
       if field.default:
         if not enum.extensible:
-          raise Exception('Non-extensible enums may not specify a default')
-        if enum.default_field is not None:
           raise Exception(
-              'Only one enumerator value may be specified as the default')
+              f'Non-extensible enum {enum.spec} may not specify a default')
+        if enum.default_field is not None:
+          raise Exception(f'Multiple [Default] enumerators in enum {enum.spec}')
         enum.default_field = field
+    # While running the backwards compatibility check, ignore errors because the
+    # old version of the enum might not specify [Default].
+    if (enum.extensible and enum.default_field is None
+        and enum.spec not in _EXTENSIBLE_ENUMS_MISSING_DEFAULT
+        and not is_running_backwards_compatibility_check_hack):
+      raise Exception(
+          f'Extensible enum {enum.spec} must specify a [Default] enumerator')
 
   module.kinds[enum.spec] = enum
 
@@ -696,6 +1009,11 @@ def _CollectReferencedKinds(module, all_defined_kinds):
         for referenced_kind in extract_referenced_user_kinds(param.kind):
           sanitized_kind = sanitize_kind(referenced_kind)
           referenced_user_kinds[sanitized_kind.spec] = sanitized_kind
+  # Consts can reference imported enums.
+  for const in module.constants:
+    if not const.kind in mojom.PRIMITIVES:
+      sanitized_kind = sanitize_kind(const.kind)
+      referenced_user_kinds[sanitized_kind.spec] = sanitized_kind
 
   return referenced_user_kinds
 
@@ -741,6 +1059,16 @@ def _AssertTypeIsStable(kind):
           assertDependencyIsStable(response_param.kind)
 
 
+def _AssertStructIsValid(kind):
+  expected_ordinals = set(range(0, len(kind.fields)))
+  ordinals = set(map(lambda field: field.ordinal, kind.fields))
+  if ordinals != expected_ordinals:
+    raise Exception(
+        'Structs must use contiguous ordinals starting from 0. ' +
+        '{} is missing the following ordinals: {}.'.format(
+            kind.mojom_name, ', '.join(map(str, expected_ordinals - ordinals))))
+
+
 def _Module(tree, path, imports):
   """
   Args:
@@ -810,8 +1138,17 @@ def _Module(tree, path, imports):
     union.fields = list(
         map(lambda field: _UnionField(module, field, union), union.fields_data))
     _AssignDefaultOrdinals(union.fields)
+    for field in union.fields:
+      if field.is_default:
+        if union.default_field is not None:
+          raise Exception('Multiple [Default] fields in union %s.' %
+                          union.mojom_name)
+        union.default_field = field
     del union.fields_data
     all_defined_kinds[union.spec] = union
+    if union.extensible and union.default_field is None:
+      raise Exception('Extensible union %s must specify a [Default] field' %
+                      union.mojom_name)
 
   for interface in module.interfaces:
     interface.methods = list(
@@ -829,8 +1166,8 @@ def _Module(tree, path, imports):
                                                  all_defined_kinds.values())
   imported_kind_specs = set(all_referenced_kinds.keys()).difference(
       set(all_defined_kinds.keys()))
-  module.imported_kinds = dict(
-      (spec, all_referenced_kinds[spec]) for spec in imported_kind_specs)
+  module.imported_kinds = OrderedDict((spec, all_referenced_kinds[spec])
+                                      for spec in sorted(imported_kind_specs))
 
   generator.AddComputedData(module)
   for iface in module.interfaces:
@@ -847,6 +1184,9 @@ def _Module(tree, path, imports):
       if kind.stable:
         _AssertTypeIsStable(kind)
 
+  for kind in module.structs:
+    _AssertStructIsValid(kind)
+
   return module
 
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
index 19905c8a..42593745 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -69,5 +69,38 @@ class TranslateTest(unittest.TestCase):
     # pylint: disable=W0212
     self.assertEquals(
         translate._MapKind("asso<SomeInterface>?"), "?asso:x:SomeInterface")
-    self.assertEquals(
-        translate._MapKind("asso<SomeInterface&>?"), "?asso:r:x:SomeInterface")
+    self.assertEquals(translate._MapKind("rca<SomeInterface>?"),
+                      "?rca:x:SomeInterface")
+
+  def testSelfRecursiveUnions(self):
+    """Verifies _UnionField() raises when a union is self-recursive."""
+    tree = ast.Mojom(None, ast.ImportList(), [
+        ast.Union("SomeUnion", None,
+                  ast.UnionBody([ast.UnionField("a", None, None, "SomeUnion")]))
+    ])
+    with self.assertRaises(Exception):
+      translate.OrderedModule(tree, "mojom_tree", [])
+
+    tree = ast.Mojom(None, ast.ImportList(), [
+        ast.Union(
+            "SomeUnion", None,
+            ast.UnionBody([ast.UnionField("a", None, None, "SomeUnion?")]))
+    ])
+    with self.assertRaises(Exception):
+      translate.OrderedModule(tree, "mojom_tree", [])
+
+  def testDuplicateAttributesException(self):
+    tree = ast.Mojom(None, ast.ImportList(), [
+        ast.Union(
+            "FakeUnion",
+            ast.AttributeList([
+                ast.Attribute("key1", "value"),
+                ast.Attribute("key1", "value")
+            ]),
+            ast.UnionBody([
+                ast.UnionField("a", None, None, "int32"),
+                ast.UnionField("b", None, None, "string")
+            ]))
+    ])
+    with self.assertRaises(Exception):
+      translate.OrderedModule(tree, "mojom_tree", [])
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py
index 1f0db200..80e8c657 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Node classes for the AST for a Mojo IDL file."""
@@ -8,17 +8,14 @@
 # and lineno). You may also define __repr__() to help with analyzing test
 # failures, especially for more complex types.
 
+import os.path
 
-import sys
 
+# Instance of 'NodeListBase' has no '_list_item_type' member (no-member)
+# pylint: disable=no-member
 
-def _IsStrOrUnicode(x):
-  if sys.version_info[0] < 3:
-    return isinstance(x, (unicode, str))
-  return isinstance(x, str)
 
-
-class NodeBase(object):
+class NodeBase:
   """Base class for nodes in the AST."""
 
   def __init__(self, filename=None, lineno=None):
@@ -43,7 +40,7 @@ class NodeListBase(NodeBase):
   classes, in a tuple) of the members of the list.)"""
 
   def __init__(self, item_or_items=None, **kwargs):
-    super(NodeListBase, self).__init__(**kwargs)
+    super().__init__(**kwargs)
     self.items = []
     if item_or_items is None:
       pass
@@ -62,7 +59,7 @@ class NodeListBase(NodeBase):
     return self.items.__iter__()
 
   def __eq__(self, other):
-    return super(NodeListBase, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.items == other.items
 
   # Implement this so that on failure, we get slightly more sensible output.
@@ -96,7 +93,7 @@ class Definition(NodeBase):
   include parameter definitions.) This class is meant to be subclassed."""
 
   def __init__(self, mojom_name, **kwargs):
-    assert _IsStrOrUnicode(mojom_name)
+    assert isinstance(mojom_name, str)
     NodeBase.__init__(self, **kwargs)
     self.mojom_name = mojom_name
 
@@ -108,13 +105,13 @@ class Attribute(NodeBase):
   """Represents an attribute."""
 
   def __init__(self, key, value, **kwargs):
-    assert _IsStrOrUnicode(key)
-    super(Attribute, self).__init__(**kwargs)
+    assert isinstance(key, str)
+    super().__init__(**kwargs)
     self.key = key
     self.value = value
 
   def __eq__(self, other):
-    return super(Attribute, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.key == other.key and \
            self.value == other.value
 
@@ -131,17 +128,17 @@ class Const(Definition):
   def __init__(self, mojom_name, attribute_list, typename, value, **kwargs):
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
     # The typename is currently passed through as a string.
-    assert _IsStrOrUnicode(typename)
+    assert isinstance(typename, str)
     # The value is either a literal (currently passed through as a string) or a
     # "wrapped identifier".
-    assert _IsStrOrUnicode or isinstance(value, tuple)
-    super(Const, self).__init__(mojom_name, **kwargs)
+    assert isinstance(value, (tuple, str))
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.typename = typename
     self.value = value
 
   def __eq__(self, other):
-    return super(Const, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.typename == other.typename and \
            self.value == other.value
@@ -153,12 +150,12 @@ class Enum(Definition):
   def __init__(self, mojom_name, attribute_list, enum_value_list, **kwargs):
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
     assert enum_value_list is None or isinstance(enum_value_list, EnumValueList)
-    super(Enum, self).__init__(mojom_name, **kwargs)
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.enum_value_list = enum_value_list
 
   def __eq__(self, other):
-    return super(Enum, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.enum_value_list == other.enum_value_list
 
@@ -170,13 +167,13 @@ class EnumValue(Definition):
     # The optional value is either an int (which is current a string) or a
     # "wrapped identifier".
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
-    assert value is None or _IsStrOrUnicode(value) or isinstance(value, tuple)
-    super(EnumValue, self).__init__(mojom_name, **kwargs)
+    assert value is None or isinstance(value, (tuple, str))
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.value = value
 
   def __eq__(self, other):
-    return super(EnumValue, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.value == other.value
 
@@ -193,13 +190,14 @@ class Import(NodeBase):
 
   def __init__(self, attribute_list, import_filename, **kwargs):
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
-    assert _IsStrOrUnicode(import_filename)
-    super(Import, self).__init__(**kwargs)
+    assert isinstance(import_filename, str)
+    super().__init__(**kwargs)
     self.attribute_list = attribute_list
-    self.import_filename = import_filename
+    # TODO(crbug.com/953884): Use pathlib once we're migrated fully to Python 3.
+    self.import_filename = os.path.normpath(import_filename).replace('\\', '/')
 
   def __eq__(self, other):
-    return super(Import, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.import_filename == other.import_filename
 
@@ -216,12 +214,12 @@ class Interface(Definition):
   def __init__(self, mojom_name, attribute_list, body, **kwargs):
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
     assert isinstance(body, InterfaceBody)
-    super(Interface, self).__init__(mojom_name, **kwargs)
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.body = body
 
   def __eq__(self, other):
-    return super(Interface, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.body == other.body
 
@@ -236,14 +234,14 @@ class Method(Definition):
     assert isinstance(parameter_list, ParameterList)
     assert response_parameter_list is None or \
            isinstance(response_parameter_list, ParameterList)
-    super(Method, self).__init__(mojom_name, **kwargs)
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.ordinal = ordinal
     self.parameter_list = parameter_list
     self.response_parameter_list = response_parameter_list
 
   def __eq__(self, other):
-    return super(Method, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.ordinal == other.ordinal and \
            self.parameter_list == other.parameter_list and \
@@ -264,12 +262,12 @@ class Module(NodeBase):
     # |mojom_namespace| is either none or a "wrapped identifier".
     assert mojom_namespace is None or isinstance(mojom_namespace, tuple)
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
-    super(Module, self).__init__(**kwargs)
+    super().__init__(**kwargs)
     self.mojom_namespace = mojom_namespace
     self.attribute_list = attribute_list
 
   def __eq__(self, other):
-    return super(Module, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.mojom_namespace == other.mojom_namespace and \
            self.attribute_list == other.attribute_list
 
@@ -281,13 +279,13 @@ class Mojom(NodeBase):
     assert module is None or isinstance(module, Module)
     assert isinstance(import_list, ImportList)
     assert isinstance(definition_list, list)
-    super(Mojom, self).__init__(**kwargs)
+    super().__init__(**kwargs)
     self.module = module
     self.import_list = import_list
     self.definition_list = definition_list
 
   def __eq__(self, other):
-    return super(Mojom, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.module == other.module and \
            self.import_list == other.import_list and \
            self.definition_list == other.definition_list
@@ -302,11 +300,11 @@ class Ordinal(NodeBase):
 
   def __init__(self, value, **kwargs):
     assert isinstance(value, int)
-    super(Ordinal, self).__init__(**kwargs)
+    super().__init__(**kwargs)
     self.value = value
 
   def __eq__(self, other):
-    return super(Ordinal, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.value == other.value
 
 
@@ -314,18 +312,18 @@ class Parameter(NodeBase):
   """Represents a method request or response parameter."""
 
   def __init__(self, mojom_name, attribute_list, ordinal, typename, **kwargs):
-    assert _IsStrOrUnicode(mojom_name)
+    assert isinstance(mojom_name, str)
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
     assert ordinal is None or isinstance(ordinal, Ordinal)
-    assert _IsStrOrUnicode(typename)
-    super(Parameter, self).__init__(**kwargs)
+    assert isinstance(typename, str)
+    super().__init__(**kwargs)
     self.mojom_name = mojom_name
     self.attribute_list = attribute_list
     self.ordinal = ordinal
     self.typename = typename
 
   def __eq__(self, other):
-    return super(Parameter, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.mojom_name == other.mojom_name and \
            self.attribute_list == other.attribute_list and \
            self.ordinal == other.ordinal and \
@@ -344,42 +342,51 @@ class Struct(Definition):
   def __init__(self, mojom_name, attribute_list, body, **kwargs):
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
     assert isinstance(body, StructBody) or body is None
-    super(Struct, self).__init__(mojom_name, **kwargs)
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.body = body
 
   def __eq__(self, other):
-    return super(Struct, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.body == other.body
 
+  def __repr__(self):
+    return "Struct(mojom_name = %s, attribute_list = %s, body = %s)" % (
+        self.mojom_name, self.attribute_list, self.body)
+
 
 class StructField(Definition):
   """Represents a struct field definition."""
 
   def __init__(self, mojom_name, attribute_list, ordinal, typename,
                default_value, **kwargs):
-    assert _IsStrOrUnicode(mojom_name)
+    assert isinstance(mojom_name, str)
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
     assert ordinal is None or isinstance(ordinal, Ordinal)
-    assert _IsStrOrUnicode(typename)
+    assert isinstance(typename, str)
     # The optional default value is currently either a value as a string or a
     # "wrapped identifier".
-    assert default_value is None or _IsStrOrUnicode(default_value) or \
-        isinstance(default_value, tuple)
-    super(StructField, self).__init__(mojom_name, **kwargs)
+    assert default_value is None or isinstance(default_value, (str, tuple))
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.ordinal = ordinal
     self.typename = typename
     self.default_value = default_value
 
   def __eq__(self, other):
-    return super(StructField, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.ordinal == other.ordinal and \
            self.typename == other.typename and \
            self.default_value == other.default_value
 
+  def __repr__(self):
+    return ("StructField(mojom_name = %s, attribute_list = %s, ordinal = %s, "
+            "typename = %s, default_value = %s") % (
+                self.mojom_name, self.attribute_list, self.ordinal,
+                self.typename, self.default_value)
+
 
 # This needs to be declared after |StructField|.
 class StructBody(NodeListBase):
@@ -394,29 +401,29 @@ class Union(Definition):
   def __init__(self, mojom_name, attribute_list, body, **kwargs):
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
     assert isinstance(body, UnionBody)
-    super(Union, self).__init__(mojom_name, **kwargs)
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.body = body
 
   def __eq__(self, other):
-    return super(Union, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.body == other.body
 
 
 class UnionField(Definition):
   def __init__(self, mojom_name, attribute_list, ordinal, typename, **kwargs):
-    assert _IsStrOrUnicode(mojom_name)
+    assert isinstance(mojom_name, str)
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
     assert ordinal is None or isinstance(ordinal, Ordinal)
-    assert _IsStrOrUnicode(typename)
-    super(UnionField, self).__init__(mojom_name, **kwargs)
+    assert isinstance(typename, str)
+    super().__init__(mojom_name, **kwargs)
     self.attribute_list = attribute_list
     self.ordinal = ordinal
     self.typename = typename
 
   def __eq__(self, other):
-    return super(UnionField, self).__eq__(other) and \
+    return super().__eq__(other) and \
            self.attribute_list == other.attribute_list and \
            self.ordinal == other.ordinal and \
            self.typename == other.typename
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
index 62798631..c3637671 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -14,11 +14,11 @@ class _TestNode(ast.NodeBase):
   """Node type for tests."""
 
   def __init__(self, value, **kwargs):
-    super(_TestNode, self).__init__(**kwargs)
+    super().__init__(**kwargs)
     self.value = value
 
   def __eq__(self, other):
-    return super(_TestNode, self).__eq__(other) and self.value == other.value
+    return super().__eq__(other) and self.value == other.value
 
 
 class _TestNodeList(ast.NodeListBase):
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py
index 3cb73c5d..b7b06bfb 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features.py
@@ -1,4 +1,4 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
+# Copyright 2018 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Helpers for processing conditionally enabled features in a mojom."""
@@ -17,8 +17,10 @@ class EnableIfError(Error):
 def _IsEnabled(definition, enabled_features):
   """Returns true if a definition is enabled.
 
-  A definition is enabled if it has no EnableIf attribute, or if the value of
-  the EnableIf attribute is in enabled_features.
+  A definition is enabled if it has no EnableIf/EnableIfNot attribute.
+  It is retained if it has an EnableIf attribute and the attribute is in
+  enabled_features. It is retained if it has an EnableIfNot attribute and the
+  attribute is not in enabled features.
   """
   if not hasattr(definition, "attribute_list"):
     return True
@@ -27,17 +29,19 @@ def _IsEnabled(definition, enabled_features):
 
   already_defined = False
   for a in definition.attribute_list:
-    if a.key == 'EnableIf':
+    if a.key == 'EnableIf' or a.key == 'EnableIfNot':
       if already_defined:
         raise EnableIfError(
             definition.filename,
-            "EnableIf attribute may only be defined once per field.",
+            "EnableIf/EnableIfNot attribute may only be set once per field.",
             definition.lineno)
       already_defined = True
 
   for attribute in definition.attribute_list:
     if attribute.key == 'EnableIf' and attribute.value not in enabled_features:
       return False
+    if attribute.key == 'EnableIfNot' and attribute.value in enabled_features:
+      return False
   return True
 
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
index aa609be7..5fc58202 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
+# Copyright 2018 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -55,6 +55,48 @@ class ConditionalFeaturesTest(unittest.TestCase):
     """
     self.parseAndAssertEqual(const_source, expected_source)
 
+  def testFilterIfNotConst(self):
+    """Test that Consts are correctly filtered."""
+    const_source = """
+      [EnableIfNot=blue]
+      const int kMyConst1 = 1;
+      [EnableIfNot=orange]
+      const double kMyConst2 = 2;
+      [EnableIf=blue]
+      const int kMyConst3 = 3;
+      [EnableIfNot=blue]
+      const int kMyConst4 = 4;
+      [EnableIfNot=purple]
+      const int kMyConst5 = 5;
+    """
+    expected_source = """
+      [EnableIfNot=orange]
+      const double kMyConst2 = 2;
+      [EnableIf=blue]
+      const int kMyConst3 = 3;
+      [EnableIfNot=purple]
+      const int kMyConst5 = 5;
+    """
+    self.parseAndAssertEqual(const_source, expected_source)
+
+  def testFilterIfNotMultipleConst(self):
+    """Test that Consts are correctly filtered."""
+    const_source = """
+      [EnableIfNot=blue]
+      const int kMyConst1 = 1;
+      [EnableIfNot=orange]
+      const double kMyConst2 = 2;
+      [EnableIfNot=orange]
+      const int kMyConst3 = 3;
+    """
+    expected_source = """
+      [EnableIfNot=orange]
+      const double kMyConst2 = 2;
+      [EnableIfNot=orange]
+      const int kMyConst3 = 3;
+    """
+    self.parseAndAssertEqual(const_source, expected_source)
+
   def testFilterEnum(self):
     """Test that EnumValues are correctly filtered from an Enum."""
     enum_source = """
@@ -91,6 +133,24 @@ class ConditionalFeaturesTest(unittest.TestCase):
     """
     self.parseAndAssertEqual(import_source, expected_source)
 
+  def testFilterIfNotImport(self):
+    """Test that imports are correctly filtered from a Mojom."""
+    import_source = """
+      [EnableIf=blue]
+      import "foo.mojom";
+      [EnableIfNot=purple]
+      import "bar.mojom";
+      [EnableIfNot=green]
+      import "baz.mojom";
+    """
+    expected_source = """
+      [EnableIf=blue]
+      import "foo.mojom";
+      [EnableIfNot=purple]
+      import "bar.mojom";
+    """
+    self.parseAndAssertEqual(import_source, expected_source)
+
   def testFilterInterface(self):
     """Test that definitions are correctly filtered from an Interface."""
     interface_source = """
@@ -175,6 +235,50 @@ class ConditionalFeaturesTest(unittest.TestCase):
     """
     self.parseAndAssertEqual(struct_source, expected_source)
 
+  def testFilterIfNotStruct(self):
+    """Test that definitions are correctly filtered from a Struct."""
+    struct_source = """
+      struct MyStruct {
+        [EnableIf=blue]
+        enum MyEnum {
+          VALUE1,
+          [EnableIfNot=red]
+          VALUE2,
+        };
+        [EnableIfNot=yellow]
+        const double kMyConst = 1.23;
+        [EnableIf=green]
+        int32 a;
+        double b;
+        [EnableIfNot=purple]
+        int32 c;
+        [EnableIf=blue]
+        double d;
+        int32 e;
+        [EnableIfNot=red]
+        double f;
+      };
+    """
+    expected_source = """
+      struct MyStruct {
+        [EnableIf=blue]
+        enum MyEnum {
+          VALUE1,
+        };
+        [EnableIfNot=yellow]
+        const double kMyConst = 1.23;
+        [EnableIf=green]
+        int32 a;
+        double b;
+        [EnableIfNot=purple]
+        int32 c;
+        [EnableIf=blue]
+        double d;
+        int32 e;
+      };
+    """
+    self.parseAndAssertEqual(struct_source, expected_source)
+
   def testFilterUnion(self):
     """Test that UnionFields are correctly filtered from a Union."""
     union_source = """
@@ -228,6 +332,30 @@ class ConditionalFeaturesTest(unittest.TestCase):
                       conditional_features.RemoveDisabledDefinitions,
                       definition, ENABLED_FEATURES)
 
+  def testMultipleEnableIfs(self):
+    source = """
+      enum Foo {
+        [EnableIf=red,EnableIfNot=yellow]
+        kBarValue = 5,
+      };
+    """
+    definition = parser.Parse(source, "my_file.mojom")
+    self.assertRaises(conditional_features.EnableIfError,
+                      conditional_features.RemoveDisabledDefinitions,
+                      definition, ENABLED_FEATURES)
+
+  def testMultipleEnableIfs(self):
+    source = """
+      enum Foo {
+        [EnableIfNot=red,EnableIfNot=yellow]
+        kBarValue = 5,
+      };
+    """
+    definition = parser.Parse(source, "my_file.mojom")
+    self.assertRaises(conditional_features.EnableIfError,
+                      conditional_features.RemoveDisabledDefinitions,
+                      definition, ENABLED_FEATURES)
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py
index 3e084bbf..73ca15df 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -22,7 +22,7 @@ class LexError(Error):
 
 # We have methods which look like they could be functions:
 # pylint: disable=R0201
-class Lexer(object):
+class Lexer:
   def __init__(self, filename):
     self.filename = filename
 
@@ -81,7 +81,6 @@ class Lexer(object):
       # Operators
       'MINUS',
       'PLUS',
-      'AMP',
       'QSTN',
 
       # Assignment
@@ -168,7 +167,6 @@ class Lexer(object):
   # Operators
   t_MINUS = r'-'
   t_PLUS = r'\+'
-  t_AMP = r'&'
   t_QSTN = r'\?'
 
   # =
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
index eadc6587..ce376da6 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -146,7 +146,6 @@ class LexerTest(unittest.TestCase):
         self._SingleTokenForInput("+"), _MakeLexToken("PLUS", "+"))
     self.assertEquals(
         self._SingleTokenForInput("-"), _MakeLexToken("MINUS", "-"))
-    self.assertEquals(self._SingleTokenForInput("&"), _MakeLexToken("AMP", "&"))
     self.assertEquals(
         self._SingleTokenForInput("?"), _MakeLexToken("QSTN", "?"))
     self.assertEquals(
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py
index b3b803d6..683ae757 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Generates a syntax tree from a Mojo IDL file."""
@@ -33,7 +33,7 @@ class ParseError(Error):
 
 # We have methods which look like they could be functions:
 # pylint: disable=R0201
-class Parser(object):
+class Parser:
   def __init__(self, lexer, source, filename):
     self.tokens = lexer.tokens
     self.source = source
@@ -140,11 +140,18 @@ class Parser(object):
     p[0].Append(p[3])
 
   def p_attribute_1(self, p):
+    """attribute : NAME EQUALS identifier_wrapped"""
+    p[0] = ast.Attribute(p[1],
+                         p[3][1],
+                         filename=self.filename,
+                         lineno=p.lineno(1))
+
+  def p_attribute_2(self, p):
     """attribute : NAME EQUALS evaled_literal
                  | NAME EQUALS NAME"""
     p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1))
 
-  def p_attribute_2(self, p):
+  def p_attribute_3(self, p):
     """attribute : NAME"""
     p[0] = ast.Attribute(p[1], True, filename=self.filename, lineno=p.lineno(1))
 
@@ -271,8 +278,7 @@ class Parser(object):
     """nonnullable_typename : basictypename
                             | array
                             | fixed_array
-                            | associative_array
-                            | interfacerequest"""
+                            | associative_array"""
     p[0] = p[1]
 
   def p_basictypename(self, p):
@@ -342,14 +348,6 @@ class Parser(object):
     """associative_array : MAP LANGLE identifier COMMA typename RANGLE"""
     p[0] = p[5] + "{" + p[3] + "}"
 
-  def p_interfacerequest(self, p):
-    """interfacerequest : identifier AMP
-                        | ASSOCIATED identifier AMP"""
-    if len(p) == 3:
-      p[0] = p[1] + "&"
-    else:
-      p[0] = "asso<" + p[2] + "&>"
-
   def p_ordinal_1(self, p):
     """ordinal : """
     p[0] = None
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
index 6d6b7153..0513343e 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -1086,7 +1086,7 @@ class ParserTest(unittest.TestCase):
           handle<data_pipe_producer>? k;
           handle<message_pipe>? l;
           handle<shared_buffer>? m;
-          some_interface&? n;
+          pending_receiver<some_interface>? n;
           handle<platform>? o;
         };
         """
@@ -1110,7 +1110,7 @@ class ParserTest(unittest.TestCase):
                 ast.StructField('l', None, None, 'handle<message_pipe>?', None),
                 ast.StructField('m', None, None, 'handle<shared_buffer>?',
                                 None),
-                ast.StructField('n', None, None, 'some_interface&?', None),
+                ast.StructField('n', None, None, 'rcv<some_interface>?', None),
                 ast.StructField('o', None, None, 'handle<platform>?', None)
             ]))
     ])
@@ -1138,16 +1138,6 @@ class ParserTest(unittest.TestCase):
         r" *handle\?<data_pipe_consumer> a;$"):
       parser.Parse(source2, "my_file.mojom")
 
-    source3 = """\
-        struct MyStruct {
-          some_interface?& a;
-        };
-        """
-    with self.assertRaisesRegexp(
-        parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected '&':\n"
-        r" *some_interface\?& a;$"):
-      parser.Parse(source3, "my_file.mojom")
-
   def testSimpleUnion(self):
     """Tests a simple .mojom source that just defines a union."""
     source = """\
@@ -1317,9 +1307,9 @@ class ParserTest(unittest.TestCase):
     source1 = """\
         struct MyStruct {
           associated MyInterface a;
-          associated MyInterface& b;
+          pending_associated_receiver<MyInterface> b;
           associated MyInterface? c;
-          associated MyInterface&? d;
+          pending_associated_receiver<MyInterface>? d;
         };
         """
     expected1 = ast.Mojom(None, ast.ImportList(), [
@@ -1327,16 +1317,16 @@ class ParserTest(unittest.TestCase):
             'MyStruct', None,
             ast.StructBody([
                 ast.StructField('a', None, None, 'asso<MyInterface>', None),
-                ast.StructField('b', None, None, 'asso<MyInterface&>', None),
+                ast.StructField('b', None, None, 'rca<MyInterface>', None),
                 ast.StructField('c', None, None, 'asso<MyInterface>?', None),
-                ast.StructField('d', None, None, 'asso<MyInterface&>?', None)
+                ast.StructField('d', None, None, 'rca<MyInterface>?', None)
             ]))
     ])
     self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
 
     source2 = """\
         interface MyInterface {
-          MyMethod(associated A a) =>(associated B& b);
+          MyMethod(associated A a) =>(pending_associated_receiver<B> b);
         };"""
     expected2 = ast.Mojom(None, ast.ImportList(), [
         ast.Interface(
@@ -1344,10 +1334,10 @@ class ParserTest(unittest.TestCase):
             ast.InterfaceBody(
                 ast.Method(
                     'MyMethod', None, None,
-                    ast.ParameterList(
-                        ast.Parameter('a', None, None, 'asso<A>')),
-                    ast.ParameterList(
-                        ast.Parameter('b', None, None, 'asso<B&>')))))
+                    ast.ParameterList(ast.Parameter('a', None, None,
+                                                    'asso<A>')),
+                    ast.ParameterList(ast.Parameter('b', None, None,
+                                                    'rca<B>')))))
     ])
     self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
 
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom_parser.py b/utils/ipc/mojo/public/tools/mojom/mojom_parser.py
index eb90c825..9693090e 100755
--- a/utils/ipc/mojo/public/tools/mojom/mojom_parser.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom_parser.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2020 The Chromium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Parses mojom IDL files.
@@ -11,6 +11,7 @@ generate usable language bindings.
 """
 
 import argparse
+import builtins
 import codecs
 import errno
 import json
@@ -19,6 +20,7 @@ import multiprocessing
 import os
 import os.path
 import sys
+import traceback
 from collections import defaultdict
 
 from mojom.generate import module
@@ -28,16 +30,12 @@ from mojom.parse import conditional_features
 
 
 # Disable this for easier debugging.
-# In Python 2, subprocesses just hang when exceptions are thrown :(.
-_ENABLE_MULTIPROCESSING = sys.version_info[0] > 2
+_ENABLE_MULTIPROCESSING = True
 
-if sys.version_info < (3, 4):
-  _MULTIPROCESSING_USES_FORK = sys.platform.startswith('linux')
-else:
-  # https://docs.python.org/3/library/multiprocessing.html#:~:text=bpo-33725
-  if __name__ == '__main__' and sys.platform == 'darwin':
-    multiprocessing.set_start_method('fork')
-  _MULTIPROCESSING_USES_FORK = multiprocessing.get_start_method() == 'fork'
+# https://docs.python.org/3/library/multiprocessing.html#:~:text=bpo-33725
+if __name__ == '__main__' and sys.platform == 'darwin':
+  multiprocessing.set_start_method('fork')
+_MULTIPROCESSING_USES_FORK = multiprocessing.get_start_method() == 'fork'
 
 
 def _ResolveRelativeImportPath(path, roots):
@@ -63,7 +61,7 @@ def _ResolveRelativeImportPath(path, roots):
   raise ValueError('"%s" does not exist in any of %s' % (path, roots))
 
 
-def _RebaseAbsolutePath(path, roots):
+def RebaseAbsolutePath(path, roots):
   """Rewrites an absolute file path as relative to an absolute directory path in
   roots.
 
@@ -139,7 +137,7 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,
     # Already done.
     return
 
-  for dep_abspath, dep_path in dependencies[mojom_abspath]:
+  for dep_abspath, dep_path in sorted(dependencies[mojom_abspath]):
     if dep_abspath not in loaded_modules:
       _EnsureInputLoaded(dep_abspath, dep_path, abs_paths, asts, dependencies,
                          loaded_modules, module_metadata)
@@ -159,11 +157,19 @@ def _CollectAllowedImportsFromBuildMetadata(build_metadata_filename):
 
   def collect(metadata_filename):
     processed_deps.add(metadata_filename)
+
+    # Paths in the metadata file are relative to the metadata file's dir.
+    metadata_dir = os.path.abspath(os.path.dirname(metadata_filename))
+
+    def to_abs(s):
+      return os.path.normpath(os.path.join(metadata_dir, s))
+
     with open(metadata_filename) as f:
       metadata = json.load(f)
       allowed_imports.update(
-          map(os.path.normcase, map(os.path.normpath, metadata['sources'])))
+          [os.path.normcase(to_abs(s)) for s in metadata['sources']])
       for dep_metadata in metadata['deps']:
+        dep_metadata = to_abs(dep_metadata)
         if dep_metadata not in processed_deps:
           collect(dep_metadata)
 
@@ -172,8 +178,7 @@ def _CollectAllowedImportsFromBuildMetadata(build_metadata_filename):
 
 
 # multiprocessing helper.
-def _ParseAstHelper(args):
-  mojom_abspath, enabled_features = args
+def _ParseAstHelper(mojom_abspath, enabled_features):
   with codecs.open(mojom_abspath, encoding='utf-8') as f:
     ast = parser.Parse(f.read(), mojom_abspath)
     conditional_features.RemoveDisabledDefinitions(ast, enabled_features)
@@ -181,8 +186,7 @@ def _ParseAstHelper(args):
 
 
 # multiprocessing helper.
-def _SerializeHelper(args):
-  mojom_abspath, mojom_path = args
+def _SerializeHelper(mojom_abspath, mojom_path):
   module_path = os.path.join(_SerializeHelper.output_root_path,
                              _GetModuleFilename(mojom_path))
   module_dir = os.path.dirname(module_path)
@@ -199,12 +203,33 @@ def _SerializeHelper(args):
     _SerializeHelper.loaded_modules[mojom_abspath].Dump(f)
 
 
-def _Shard(target_func, args, processes=None):
-  args = list(args)
+class _ExceptionWrapper:
+  def __init__(self):
+    # Do not capture exception object to ensure pickling works.
+    self.formatted_trace = traceback.format_exc()
+
+
+class _FuncWrapper:
+  """Marshals exceptions and spreads args."""
+
+  def __init__(self, func):
+    self._func = func
+
+  def __call__(self, args):
+    # multiprocessing does not gracefully handle excptions.
+    # https://crbug.com/1219044
+    try:
+      return self._func(*args)
+    except:  # pylint: disable=bare-except
+      return _ExceptionWrapper()
+
+
+def _Shard(target_func, arg_list, processes=None):
+  arg_list = list(arg_list)
   if processes is None:
     processes = multiprocessing.cpu_count()
   # Seems optimal to have each process perform at least 2 tasks.
-  processes = min(processes, len(args) // 2)
+  processes = min(processes, len(arg_list) // 2)
 
   if sys.platform == 'win32':
     # TODO(crbug.com/1190269) - we can't use more than 56
@@ -213,13 +238,17 @@ def _Shard(target_func, args, processes=None):
 
   # Don't spin up processes unless there is enough work to merit doing so.
   if not _ENABLE_MULTIPROCESSING or processes < 2:
-    for result in map(target_func, args):
-      yield result
+    for arg_tuple in arg_list:
+      yield target_func(*arg_tuple)
     return
 
   pool = multiprocessing.Pool(processes=processes)
   try:
-    for result in pool.imap_unordered(target_func, args):
+    wrapped_func = _FuncWrapper(target_func)
+    for result in pool.imap_unordered(wrapped_func, arg_list):
+      if isinstance(result, _ExceptionWrapper):
+        sys.stderr.write(result.formatted_trace)
+        sys.exit(1)
       yield result
   finally:
     pool.close()
@@ -230,6 +259,7 @@ def _Shard(target_func, args, processes=None):
 def _ParseMojoms(mojom_files,
                  input_root_paths,
                  output_root_path,
+                 module_root_paths,
                  enabled_features,
                  module_metadata,
                  allowed_imports=None):
@@ -245,8 +275,10 @@ def _ParseMojoms(mojom_files,
         are based on the mojom's relative path, rebased onto this path.
         Additionally, the script expects this root to contain already-generated
         modules for any transitive dependencies not listed in mojom_files.
+    module_root_paths: A list of absolute filesystem paths which contain
+        already-generated modules for any non-transitive dependencies.
     enabled_features: A list of enabled feature names, controlling which AST
-        nodes are filtered by [EnableIf] attributes.
+        nodes are filtered by [EnableIf] or [EnableIfNot] attributes.
     module_metadata: A list of 2-tuples representing metadata key-value pairs to
         attach to each compiled module output.
 
@@ -262,7 +294,7 @@ def _ParseMojoms(mojom_files,
   loaded_modules = {}
   input_dependencies = defaultdict(set)
   mojom_files_to_parse = dict((os.path.normcase(abs_path),
-                               _RebaseAbsolutePath(abs_path, input_root_paths))
+                               RebaseAbsolutePath(abs_path, input_root_paths))
                               for abs_path in mojom_files)
   abs_paths = dict(
       (path, abs_path) for abs_path, path in mojom_files_to_parse.items())
@@ -274,7 +306,7 @@ def _ParseMojoms(mojom_files,
     loaded_mojom_asts[mojom_abspath] = ast
 
   logging.info('Processing dependencies')
-  for mojom_abspath, ast in loaded_mojom_asts.items():
+  for mojom_abspath, ast in sorted(loaded_mojom_asts.items()):
     invalid_imports = []
     for imp in ast.import_list:
       import_abspath = _ResolveRelativeImportPath(imp.import_filename,
@@ -295,8 +327,8 @@ def _ParseMojoms(mojom_files,
         # be parsed and have a module file sitting in a corresponding output
         # location.
         module_path = _GetModuleFilename(imp.import_filename)
-        module_abspath = _ResolveRelativeImportPath(module_path,
-                                                    [output_root_path])
+        module_abspath = _ResolveRelativeImportPath(
+            module_path, module_root_paths + [output_root_path])
         with open(module_abspath, 'rb') as module_file:
           loaded_modules[import_abspath] = module.Module.Load(module_file)
 
@@ -370,6 +402,15 @@ already present in the provided output root.""")
       'based on the relative input path, rebased onto this root. Note that '
       'ROOT is also searched for existing modules of any transitive imports '
       'which were not included in the set of inputs.')
+  arg_parser.add_argument(
+      '--module-root',
+      default=[],
+      action='append',
+      metavar='ROOT',
+      dest='module_root_paths',
+      help='Adds ROOT to the set of root paths to search for existing modules '
+      'of non-transitive imports. Provided root paths are always searched in '
+      'order from longest absolute path to shortest.')
   arg_parser.add_argument(
       '--mojoms',
       nargs='+',
@@ -396,9 +437,9 @@ already present in the provided output root.""")
       help='Enables a named feature when parsing the given mojoms. Features '
       'are identified by arbitrary string values. Specifying this flag with a '
       'given FEATURE name will cause the parser to process any syntax elements '
-      'tagged with an [EnableIf=FEATURE] attribute. If this flag is not '
-      'provided for a given FEATURE, such tagged elements are discarded by the '
-      'parser and will not be present in the compiled output.')
+      'tagged with an [EnableIf=FEATURE] or [EnableIfNot] attribute. If this '
+      'flag is not provided for a given FEATURE, such tagged elements are '
+      'discarded by the parser and will not be present in the compiled output.')
   arg_parser.add_argument(
       '--check-imports',
       dest='build_metadata_filename',
@@ -436,6 +477,7 @@ already present in the provided output root.""")
   mojom_files = list(map(os.path.abspath, args.mojom_files))
   input_roots = list(map(os.path.abspath, args.input_root_paths))
   output_root = os.path.abspath(args.output_root_path)
+  module_roots = list(map(os.path.abspath, args.module_root_paths))
 
   if args.build_metadata_filename:
     allowed_imports = _CollectAllowedImportsFromBuildMetadata(
@@ -445,13 +487,16 @@ already present in the provided output root.""")
 
   module_metadata = list(
       map(lambda kvp: tuple(kvp.split('=')), args.module_metadata))
-  _ParseMojoms(mojom_files, input_roots, output_root, args.enabled_features,
-               module_metadata, allowed_imports)
+  _ParseMojoms(mojom_files, input_roots, output_root, module_roots,
+               args.enabled_features, module_metadata, allowed_imports)
   logging.info('Finished')
-  # Exit without running GC, which can save multiple seconds due the large
-  # number of object created.
-  os._exit(0)
 
 
 if __name__ == '__main__':
   Run(sys.argv[1:])
+  # Exit without running GC, which can save multiple seconds due to the large
+  # number of object created. But flush is necessary as os._exit doesn't do
+  # that.
+  sys.stdout.flush()
+  sys.stderr.flush()
+  os._exit(0)
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom_parser_test_case.py b/utils/ipc/mojo/public/tools/mojom/mojom_parser_test_case.py
index e213fbfa..45803ebe 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom_parser_test_case.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom_parser_test_case.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -20,7 +20,7 @@ class MojomParserTestCase(unittest.TestCase):
   resolution, and module serialization and deserialization."""
 
   def __init__(self, method_name):
-    super(MojomParserTestCase, self).__init__(method_name)
+    super().__init__(method_name)
     self._temp_dir = None
 
   def setUp(self):
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom_parser_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom_parser_unittest.py
index a93f34ba..353a2b6e 100644
--- a/utils/ipc/mojo/public/tools/mojom/mojom_parser_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/mojom_parser_unittest.py
@@ -1,7 +1,9 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import json
+
 from mojom_parser_test_case import MojomParserTestCase
 
 
@@ -119,15 +121,22 @@ class MojomParserTest(MojomParserTestCase):
     c = 'c.mojom'
     c_metadata = 'out/c.build_metadata'
     self.WriteFile(a_metadata,
-                   '{"sources": ["%s"], "deps": []}\n' % self.GetPath(a))
+                   json.dumps({
+                       "sources": [self.GetPath(a)],
+                       "deps": []
+                   }))
     self.WriteFile(
         b_metadata,
-        '{"sources": ["%s"], "deps": ["%s"]}\n' % (self.GetPath(b),
-                                                   self.GetPath(a_metadata)))
+        json.dumps({
+            "sources": [self.GetPath(b)],
+            "deps": [self.GetPath(a_metadata)]
+        }))
     self.WriteFile(
         c_metadata,
-        '{"sources": ["%s"], "deps": ["%s"]}\n' % (self.GetPath(c),
-                                                   self.GetPath(b_metadata)))
+        json.dumps({
+            "sources": [self.GetPath(c)],
+            "deps": [self.GetPath(b_metadata)]
+        }))
     self.WriteFile(a, """\
         module a;
         struct Bar {};""")
@@ -154,9 +163,15 @@ class MojomParserTest(MojomParserTestCase):
     b = 'b.mojom'
     b_metadata = 'out/b.build_metadata'
     self.WriteFile(a_metadata,
-                   '{"sources": ["%s"], "deps": []}\n' % self.GetPath(a))
+                   json.dumps({
+                       "sources": [self.GetPath(a)],
+                       "deps": []
+                   }))
     self.WriteFile(b_metadata,
-                   '{"sources": ["%s"], "deps": []}\n' % self.GetPath(b))
+                   json.dumps({
+                       "sources": [self.GetPath(b)],
+                       "deps": []
+                   }))
     self.WriteFile(a, """\
         module a;
         struct Bar {};""")
diff --git a/utils/ipc/mojo/public/tools/mojom/stable_attribute_unittest.py b/utils/ipc/mojo/public/tools/mojom/stable_attribute_unittest.py
index d45ec586..d10d69c6 100644
--- a/utils/ipc/mojo/public/tools/mojom/stable_attribute_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/stable_attribute_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/utils/ipc/mojo/public/tools/mojom/union_unittest.py b/utils/ipc/mojo/public/tools/mojom/union_unittest.py
new file mode 100644
index 00000000..6b2525e5
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/mojom/union_unittest.py
@@ -0,0 +1,44 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from mojom_parser_test_case import MojomParserTestCase
+
+
+class UnionTest(MojomParserTestCase):
+  """Tests union parsing behavior."""
+
+  def testExtensibleMustHaveDefault(self):
+    """Verifies that extensible unions must have a default field."""
+    mojom = 'foo.mojom'
+    self.WriteFile(mojom, 'module foo; [Extensible] union U { bool x; };')
+    with self.assertRaisesRegexp(Exception, 'must specify a \[Default\]'):
+      self.ParseMojoms([mojom])
+
+  def testExtensibleSingleDefault(self):
+    """Verifies that extensible unions must not have multiple default fields."""
+    mojom = 'foo.mojom'
+    self.WriteFile(
+        mojom, """\
+               module foo;
+               [Extensible] union U {
+                 [Default] bool x;
+                 [Default] bool y;
+               };
+               """)
+    with self.assertRaisesRegexp(Exception, 'Multiple \[Default\] fields'):
+      self.ParseMojoms([mojom])
+
+  def testExtensibleDefaultTypeValid(self):
+    """Verifies that an extensible union's default field must be nullable or
+    integral type."""
+    mojom = 'foo.mojom'
+    self.WriteFile(
+        mojom, """\
+               module foo;
+               [Extensible] union U {
+                 [Default] handle<message_pipe> p;
+               };
+               """)
+    with self.assertRaisesRegexp(Exception, 'must be nullable or integral'):
+      self.ParseMojoms([mojom])
diff --git a/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py b/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py
index 65db4dc9..7b71ce65 100644
--- a/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py
+++ b/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -23,7 +23,7 @@ class VersionCompatibilityTest(MojomParserTestCase):
 
     checker = module.BackwardCompatibilityChecker()
     compatibility_map = {}
-    for name in old.keys():
+    for name in old:
       compatibility_map[name] = checker.IsBackwardCompatible(
           new[name], old[name])
     return compatibility_map
@@ -60,40 +60,48 @@ class VersionCompatibilityTest(MojomParserTestCase):
     """Adding a value to an existing version is not allowed, even if the old
     enum was marked [Extensible]. Note that it is irrelevant whether or not the
     new enum is marked [Extensible]."""
-    self.assertNotBackwardCompatible('[Extensible] enum E { kFoo, kBar };',
-                                     'enum E { kFoo, kBar, kBaz };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kFoo, kBar };',
-        '[Extensible] enum E { kFoo, kBar, kBaz };')
+        '[Extensible] enum E { [Default] kFoo, kBar };',
+        'enum E { kFoo, kBar, kBaz };')
+    self.assertNotBackwardCompatible(
+        '[Extensible] enum E { [Default] kFoo, kBar };',
+        '[Extensible] enum E { [Default] kFoo, kBar, kBaz };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kFoo, [MinVersion=1] kBar };',
+        '[Extensible] enum E { [Default] kFoo, [MinVersion=1] kBar };',
         'enum E { kFoo, [MinVersion=1] kBar, [MinVersion=1] kBaz };')
 
   def testEnumValueRemoval(self):
     """Removal of an enum value is never valid even for [Extensible] enums."""
     self.assertNotBackwardCompatible('enum E { kFoo, kBar };',
                                      'enum E { kFoo };')
-    self.assertNotBackwardCompatible('[Extensible] enum E { kFoo, kBar };',
-                                     '[Extensible] enum E { kFoo };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kA, [MinVersion=1] kB };',
-        '[Extensible] enum E { kA, };')
+        '[Extensible] enum E { [Default] kFoo, kBar };',
+        '[Extensible] enum E { [Default] kFoo };')
+    self.assertNotBackwardCompatible(
+        '[Extensible] enum E { [Default] kA, [MinVersion=1] kB };',
+        '[Extensible] enum E { [Default] kA, };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kA, [MinVersion=1] kB, [MinVersion=1] kZ };',
-        '[Extensible] enum E { kA, [MinVersion=1] kB };')
+        """[Extensible] enum E {
+          [Default] kA,
+          [MinVersion=1] kB,
+          [MinVersion=1] kZ };""",
+        '[Extensible] enum E { [Default] kA, [MinVersion=1] kB };')
 
   def testNewExtensibleEnumValueWithMinVersion(self):
     """Adding a new and properly [MinVersion]'d value to an [Extensible] enum
     is a backward-compatible change. Note that it is irrelevant whether or not
     the new enum is marked [Extensible]."""
-    self.assertBackwardCompatible('[Extensible] enum E { kA, kB };',
+    self.assertBackwardCompatible('[Extensible] enum E { [Default] kA, kB };',
                                   'enum E { kA, kB, [MinVersion=1] kC };')
     self.assertBackwardCompatible(
-        '[Extensible] enum E { kA, kB };',
-        '[Extensible] enum E { kA, kB, [MinVersion=1] kC };')
+        '[Extensible] enum E { [Default] kA, kB };',
+        '[Extensible] enum E { [Default] kA, kB, [MinVersion=1] kC };')
     self.assertBackwardCompatible(
-        '[Extensible] enum E { kA, [MinVersion=1] kB };',
-        '[Extensible] enum E { kA, [MinVersion=1] kB, [MinVersion=2] kC };')
+        '[Extensible] enum E { [Default] kA, [MinVersion=1] kB };',
+        """[Extensible] enum E {
+          [Default] kA,
+          [MinVersion=1] kB,
+          [MinVersion=2] kC };""")
 
   def testRenameEnumValue(self):
     """Renaming an enum value does not affect backward-compatibility. Only
@@ -161,14 +169,17 @@ class VersionCompatibilityTest(MojomParserTestCase):
         'struct S {}; struct T { S s; };',
         'struct S { [MinVersion=1] int32 x; }; struct T { S s; };')
     self.assertBackwardCompatible(
-        '[Extensible] enum E { kA }; struct S { E e; };',
-        '[Extensible] enum E { kA, [MinVersion=1] kB }; struct S { E e; };')
+        '[Extensible] enum E { [Default] kA }; struct S { E e; };',
+        """[Extensible] enum E {
+          [Default] kA,
+          [MinVersion=1] kB };
+          struct S { E e; };""")
     self.assertNotBackwardCompatible(
         'struct S {}; struct T { S s; };',
         'struct S { int32 x; }; struct T { S s; };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kA }; struct S { E e; };',
-        '[Extensible] enum E { kA, kB }; struct S { E e; };')
+        '[Extensible] enum E { [Default] kA }; struct S { E e; };',
+        '[Extensible] enum E { [Default] kA, kB }; struct S { E e; };')
 
   def testNewStructFieldWithInvalidMinVersion(self):
     """Adding a new field using an existing MinVersion breaks backward-
@@ -305,14 +316,17 @@ class VersionCompatibilityTest(MojomParserTestCase):
         'struct S {}; union U { S s; };',
         'struct S { [MinVersion=1] int32 x; }; union U { S s; };')
     self.assertBackwardCompatible(
-        '[Extensible] enum E { kA }; union U { E e; };',
-        '[Extensible] enum E { kA, [MinVersion=1] kB }; union U { E e; };')
+        '[Extensible] enum E { [Default] kA }; union U { E e; };',
+        """[Extensible] enum E {
+          [Default] kA,
+          [MinVersion=1] kB };
+          union U { E e; };""")
     self.assertNotBackwardCompatible(
         'struct S {}; union U { S s; };',
         'struct S { int32 x; }; union U { S s; };')
     self.assertNotBackwardCompatible(
-        '[Extensible] enum E { kA }; union U { E e; };',
-        '[Extensible] enum E { kA, kB }; union U { E e; };')
+        '[Extensible] enum E { [Default] kA }; union U { E e; };',
+        '[Extensible] enum E { [Default] kA, kB }; union U { E e; };')
 
   def testNewUnionFieldWithInvalidMinVersion(self):
     """Adding a new field using an existing MinVersion breaks backward-
diff --git a/utils/ipc/mojo/public/tools/run_all_python_unittests.py b/utils/ipc/mojo/public/tools/run_all_python_unittests.py
index b2010958..98bce18c 100755
--- a/utils/ipc/mojo/public/tools/run_all_python_unittests.py
+++ b/utils/ipc/mojo/public/tools/run_all_python_unittests.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2020 The Chromium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -8,11 +8,13 @@ import sys
 
 _TOOLS_DIR = os.path.dirname(__file__)
 _MOJOM_DIR = os.path.join(_TOOLS_DIR, 'mojom')
+_BINDINGS_DIR = os.path.join(_TOOLS_DIR, 'bindings')
 _SRC_DIR = os.path.join(_TOOLS_DIR, os.path.pardir, os.path.pardir,
                         os.path.pardir)
 
 # Ensure that the mojom library is discoverable.
 sys.path.append(_MOJOM_DIR)
+sys.path.append(_BINDINGS_DIR)
 
 # Help Python find typ in //third_party/catapult/third_party/typ/
 sys.path.append(
@@ -21,7 +23,7 @@ import typ
 
 
 def Main():
-  return typ.main(top_level_dir=_MOJOM_DIR)
+  return typ.main(top_level_dirs=[_MOJOM_DIR, _BINDINGS_DIR])
 
 
 if __name__ == '__main__':
diff --git a/utils/ipc/tools/README b/utils/ipc/tools/README
index d5c24fc3..9a2979d3 100644
--- a/utils/ipc/tools/README
+++ b/utils/ipc/tools/README
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: CC0-1.0
 
-Files in this directory are imported from 9c138d992bfc of Chromium. Do not
+Files in this directory are imported from e2b2277a00e37 of Chromium. Do not
 modify them manually.
diff --git a/utils/ipc/tools/diagnosis/crbug_1001171.py b/utils/ipc/tools/diagnosis/crbug_1001171.py
index 478fb8c1..40900d10 100644
--- a/utils/ipc/tools/diagnosis/crbug_1001171.py
+++ b/utils/ipc/tools/diagnosis/crbug_1001171.py
@@ -1,4 +1,4 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
+# Copyright 2019 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-- 
2.40.0.348.gf938b09366-goog



More information about the libcamera-devel mailing list